uniapp-手機登錄+退出登錄+第三方登錄


29 手機登錄+退出登錄+第三方登錄

一 賬號郵箱手機登錄

效果圖:

image-20200424180327708

image-20200424180311005

思路:

1 我的頁面根據loginStatus 選擇是否顯示登錄選項。

2 大前提:沒有明顯的注冊邏輯,直接手機號獲取驗證碼可以登錄,相當於直接注冊了。

3 button的登錄的時候要配置lodding 顯示加載效果以及無法點擊button按鈕

4 兩種登錄方式比較像可以考慮直接進行代碼整合(見代碼)

5 app.vue 第一次加載的時候 初始化用戶的登錄狀態(就是去緩存里取出來用戶信息和loginStatus設置為true)。

store.js

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

Vue.use(Vuex)

export default new Vuex.Store({
	state:{
		loginStatus:false,
		user:{
	// 		"id": 400,
	// 		"username": "17621868045",
	// 		"userpic": null,
	// 		"password": false,
	// 		"phone": "17621868045",
	// 		"email": null,
	// 		"status": 1,
	// 		"create_time": 1587717384,
	// 		"logintype": "phone",
	// 		"token": "3e80c968d8b5610b9fda2341fd0ba43b39c59a40",
	// 		"userinfo": {
	// 			"id": 390,
	// 			"user_id": 400,
	// 			"age": 0,
	// 			"sex": 2,
	// 			"qg": 0,
	// 			"job": null,
	// 			"path": null,
	// 			"birthday": null
	// 		}
		}

	},
	mutations:{
		// 登錄
		login(state,user){
			// 更改state中的變量要在這里更改。
			state.loginStatus = true
			state.user = user
			// 永久存儲
			uni.setStorageSync('user',JSON.stringify(user));
		},
		
		// 初始化用戶登錄狀態
		initUser(state){
			let user = uni.getStorageSync('user');
			if (user){
				state.user = JSON.parse(user)
				state.loginStatus = true
			}
		}
			
		}
		
		
		
	}
})

My.vue

<template>
	<view >
		<!-- 為了測試login頁面 -->
		<!--
			
		</navigator> -->
		<template v-if="!loginStatus">
			<view class="flex align-center justify-center font font-weight-bold my-2">
				登錄社區,體驗更多功能
			</view>
			<outer-login></outer-login>
			<view class="flex align-center justify-center font text-secondary my-2" 
			hover-class="text-main"
			@click="openLogin">
				賬號/郵箱/手機登錄 <text class="ml-1 iconfont icon-jinru"></text>
			</view>
		</template>
	
		<view v-else class="flex px-2 py-3 align-center">
			<image :src="avator" class="rounded-circle mr-2" style="height: 100rpx;width: 100rpx;" mode=""></image>
			<view class="flex-1 flex flex-column ">
				<text class="font-lg font-weight-bold">{{user.username}}</text>
				<view class="font text-muted">
					<text class="mr-3 ">總帖子 1</text>
					<text>今日發帖 0</text>
				</view>
			</view>
			<text class="iconfont icon-jinru font-lg"></text>
		</view>
		
		
		<view class="flex align-center px-2">
			<view class="flex-1 flex flex-column align-center justify-center"
			v-for="(item,index) in myData" :key="index">
				<text class="font-lg font-weight-bold">{{item.num}}</text>
				<text class="font text-muted">{{item.name}}</text>
			</view>
		</view>
		
		<!-- 廣告欄 -->
		<view class="px-2 py-3">
			<image src="/static/demo/banner2.jpg" 
			class="w-100 rounded"
			style="height: 170rpx;" mode="aspectFill"></image>
		</view>
		
		<!-- 三個小列表 -->
		<uni-list-item :show-extra-icon="true"  title="瀏覽歷史">
			<text slot="myicon" class="iconfont icon-liulan"></text>
		</uni-list-item>
		<uni-list-item :show-extra-icon="true"  title="社區認證">
			<text slot="myicon" class="iconfont icon-huiyuanvip"></text>
		</uni-list-item>
		<uni-list-item :show-extra-icon="true"  title="審核帖子">
			<text slot="myicon" class="iconfont icon-keyboard"></text>
		</uni-list-item>
		
	</view>
</template>

<script>
	import uniListItem from '@/components/uni-ui/uni-list-item/uni-list-item.vue'
	// 導入vuex的助手函數
	import outerLogin from '@/components/common/outer-login.vue';
	import { mapState } from 'vuex'
	export default {
		components:{
			uniListItem,
			outerLogin
			
		},
		data() {
			return {
				myData:[{
					name:"帖子",
					num:1
				},{
					name:"動態",
					num:1
				},{
					name:"評論",
					num:2
				},{
					name:"粉絲",
					num:0
				}]
				
			}
		},
		// 監聽導航條按鈕
		onNavigationBarButtonTap(){
			uni.navigateTo({
				url:'../user-set/user-set'
			})
		},
		computed:{
			// 看這里 看這里 看這里
			// 1 放到計算屬性是為了更精准的監聽state里面的變化
			// 2 放到mapState里面是為了少些幾行代碼,相當於語法糖吧更加方便管理(當然了直接寫成普通的計算屬性也行)
			// 3 為了和其他的計算屬性不沖突需要+...進行打散
			...mapState({
				// 4 這相當於loginStatus 是vue的那個loginstatus了 ,他地方就可以this.調用
				loginStatus:state=>state.loginStatus,
				user:state=>state.user
				// 5 如果名字一樣也可以 不用對象可以直接寫...mapState(['loginStatus'])
			}),
			// 6 多說一點改變vuex里面的state里面的數據,一定要用commit的形式,方便vue追蹤數據變化。
			avator(){
				return this.user.userpic ? this.user.userpic : '/static/mv.png'
			}
		},
		methods: {
			// 打開登錄頁
			openLogin(){
				uni.navigateTo({
					url: '../login/login',
				});
			}
		}
	}
</script>

<style>

</style>

loggin.vue

<template>
	<view>
		<!-- 狀態欄占位 -->
		<uni-status-bar></uni-status-bar>
		<!-- 左上角的叉 -->
		<view class="iconfont font-weight-bold icon-guanbi flex align-center justify-center font-lg" 
		style="width: 100rpx; height: 100rpx;"
		hover-class="text-muted"
		@click="back"></view>
		
		<!-- 賬號密碼登錄 -->
		<template v-if="status">
			<view class="font-lgger flex align-center justify-center" style="margin-top:100rpx;">
				賬號密碼登錄
			</view>
			<view class="px-2">
				<input class=" border-bottom font-md p-3" type="text"
				style="margin-top:100rpx"
				value="" placeholder="昵稱/手機號/郵箱"
				 v-model="username"/>
			
				<!-- align-stretch直接保證了各個元素高度的統一,就不只是劇中對齊了 -->
				<view class="flex align-stretch border-bottom ">
					<input class="flex-1 font-md p-3" type="text"
					value="" placeholder="請輸入密碼" 
					v-model="password"/>
					<view class="text-muted flex  align-center justify-center" style="width: 160rpx;">
						忘記密碼
					</view>
				</view>
			</view>
		</template>
		
		
		<!-- 短信驗證登錄 -->
		<template v-else>
			<view class="font-lgger flex align-center justify-center" style="margin-top:100rpx;">
				短信驗證碼登錄
			</view>
			<view class="px-2">
				<!-- 手機號 -->
				<view class="flex align-center border-bottom" style="margin-top:100rpx">
					<view class="flex align-center justify-center font-weight-bold  font-md">
						+86
					</view>
					<input class="font-md p-3" type="text"
					value="" placeholder="手機號" 
					v-model="phone"/>
					<!-- <view class="flex align-center justify-center font-weight-bold bg-main">
						+86
					</view> -->
				</view>
				
				<!-- 驗證碼 -->
				<!-- align-stretch直接保證了各個元素高度的統一,就不只是劇中對齊了 -->
				<view class="flex align-center border-bottom ">
					<input class="flex-1 font-md p-3" type="text"
					value="" placeholder="請輸入驗證碼" 
					v-model="code"/>
					<view class="text-white rounded flex font-md p-1 align-center justify-center bg-main " style="width: 180rpx;"
					@click="getCode"
					:class="codeTime>0 ?'bg-main-disabled':'bg-main'">
						{{codeTime>0 ? codeTime: '獲取驗證碼'}}
					</view>
				</view>
			</view>
		</template>
		
		
		<!-- 登錄按鈕 -->
		<view class="px-3 " style="padding-top:60rpx">
			<button class=" text-white" style="border-radius: 50rpx;border: 0;" type="primary"
			:disabled="disabled"
			:class="disabled?'bg-main-disabled':'bg-main'"
			@click="submit"
			:loading="loading">{{loading?loading:'登錄'}}</button>
			<!-- 這個loading bool一旦為true 會顯示旋轉加載狀態並且不可點擊 -->
		</view>
		
		
		
		<view class="flex align-center justify-center mx-2 my-2">
			<view class="text-primary font py-2 mx-2 " @click="changeStatus">{{status?'短信驗證登錄':'賬號密碼登錄'}}</view>
			<text>|</text>
			<view class="text-primary font py-2 mx-2">登錄遇到問題</view>
		</view>
		
		<view class="flex align-center justify-center py-2">
			<view class="" style="height: 1rpx;background-color: #CCCCCC;width: 100rpx;"></view>
			<view class="text-muted font">
				社交賬號登錄
			</view>
			<view class="" style="height: 1rpx;background-color: #CCCCCC;width: 100rpx;"></view>
		</view>
		
		<!-- 其他登錄方式 -->
		<other-login></other-login>
		
		
		<view class="flex align-center justify-center mt-2">
			<text class="text-muted font">注冊即代表您同意</text>
			<text class="font text-primary">《xxx社區協議》</text>
		</view>

		<!-- <view class="">
			
		</view> -->
	</view>
</template>

<script>
	import uniStatusBar from '@/components/uni-ui/uni-status-bar/uni-status-bar.vue'
	import otherLogin from '@/components/common/outer-login.vue'
	export default {
		components:{
			uniStatusBar,
			otherLogin
			
		},
		data() {
			return {
				status:true,// 賬號密碼驗證
				username:"",
				password:"",
				phone:"",
				code:"",
				codeTime:0,
				loading:false
				
			}
		},
		computed:{
			
			disabled(){
				if((this.username===''||this.password==='')&&(this.phone===''||this.code==='')){
					return true
				}
				return false
			}
		},
		methods: {
			// 后退1頁
			back(){
				uni.navigateBack({
					delta:1
				})
			},
			// 初始化表單
			initForm(){
				this.username = ''
				this.password = ''
				this.phone = ''
				this.code = ''
				
			},
			// 切換賬號密碼登錄or短信登錄
			changeStatus(){
				this.status = !this.status
				
			},
			// 獲取驗證碼
			getCode(){
				// 防止重復獲取
				if (this.codeTime>0){
					return;
				}
				// 驗證手機號
				if (!this.validate()) return;
				// 請求數據
				this.$H.post('/user/sendcode',{
					phone:this.phone
				},{
					// 這樣原生數據就會傳輸過來
					native:true
				}).then(res=>{
					// console.log(res)
					uni.showToast({
						title:res.data.msg,
						icon:'none'
					})
				}).catch(err=>{
					// console.log(err)
				})
				
				// 倒計時
				this.codeTime = 5
				// 箭頭函數可以直接拿到外面的this的內容
				let timer = setInterval(()=>{
					if (this.codeTime >= 1){
						this.codeTime--
					} else{
						clearInterval(timer)
					}
				},1000)
				
			},
			// 表單驗證
			validate(){
				//手機號正則 只是針對驗證碼的手機號字段,其他並未做兼容。
				var mPattern = /^1[34578]\d{9}$/; 
				if (!mPattern.test(this.phone)) {
					uni.showToast({
						title: '手機號格式不正確',
						icon: 'none'
					});
					return false
				}
				// ...更多驗證
				return true
			},
			// 提交
			submit(){
				this.loading = '登錄中...'
				let url = ""
				let data = ""
				// 表單驗證
				if(!this.status){
					if (!this.validate()) return;
				}
				if (this.status){
					// 賬號密碼登錄
					url = '/user/login'
					data = {
						username:this.username,
						password:this.password
					}
				}else{
					
					// 手機驗證碼登錄
					url = '/user/phonelogin'
					data = {
						phone:this.phone,
						code:this.code
					}
				}
				
				// 提交后端
				this.$H.post(url,data).then(res=>{
					// console.log(res)
					// 修改vuex的state,持久化存儲
					this.$store.commit('login',res)
					// 提示和跳轉
					uni.navigateBack({
						delta:1
					})
					uni.showToast({
						title:'登錄成功',
						icon:'none'
					})
					this.loading = ''
				}).catch(
					this.loading = ''
				)
				// 登錄成功處理
			}
		
		}
	}
</script>

<style>

</style>

App.vue

<script>
export default {
	onLaunch: function() {
		console.log('App Launch');
		// 檢測更新
		this.$U.update()
		// 網絡更新
		this.$U.onNetWork()
		// 初始化數據
		// 初始化用戶登錄狀態
		this.$store.commit('initUser')
	},
	onShow: function() {
		console.log('App Show');
	},
	onHide: function() {
		console.log('App Hide');
	}
};
</script>

<style>
	/*每個頁面公共css */
	/* 官方css庫 */
	@import "./common/uni.css";
	/* 自定義圖標庫 */
	@import "./common/icon.css";
	/* 動畫庫 */
	@import "./common/animate.css";
	/* @import url("./common/ceshi.css"); */
	/* 引入自定義的css庫 */
	@import "./common/free.css";
	/* 引入自定義本項目相關的css庫 */
	@import "./common/common.css";
/* 解決頭條小程序組件內引入字體不生效的問題 */
/* #ifdef MP-TOUTIAO */
@font-face {
	font-family: uniicons;
	src: url('/static/uni.ttf');
}
/* #endif */


	
</style>

二 退出登錄

效果圖:

image-20200424180114994

image-20200424180134925

點擊確定回到了首頁,再次進到設置里一看只有這兩個功能了。

image-20200424180022978

思路:

1 既然有退出登錄,就要考慮設置里面哪些是登錄了才可以顯示的。

2 退出登錄就是設置loginState的狀態為false、user={}, 清除本地緩存。

User-set.vue

<template>
	<view>
		<!-- // 登錄可見 -->
		<template v-if="loginStatus">
			<uni-list-item title="賬號與安全" @click="open('user-password')"></uni-list-item>
			<uni-list-item title="綁定郵箱" @click="open('user-email')"></uni-list-item>
			<uni-list-item title="資料編輯" @click="open('user-userinfo')"></uni-list-item>
		</template>
		
		<!-- // 非登錄可見 -->
		<uni-list-item title="清除緩存" @click="clear" class="text-muted"><text slot="right_content">{{currentSize | formatSize}}</text></uni-list-item>
		
		<!-- // 登錄可見 -->
		<uni-list-item v-if="loginStatus" title="意見反饋" @click="open('user-feedback')"></uni-list-item>
		
		<!-- // 非登錄可見 -->
		<uni-list-item title="關於社區" @click="open('about')"></uni-list-item>
		
		<!-- // 登錄可見 -->
		<view  v-if="loginStatus" class="py-3 px-2">
			<button type="primary" 
			class="bg-main rounded text-white"  
			style="border-radius: 50rpx;"
			@click="logout">退出登陸</button>
		</view>
	</view>
</template>

<script>
	import uniListItem from '@/components/uni-ui/uni-list-item/uni-list-item.vue'
	import {mapState} from 'vuex'
	export default {
		components:{
			uniListItem
		},
		data() {
			return {
				currentSize:0
			}
		},
		computed:{
			...mapState({
				loginStatus:state=>state.loginStatus
			})
		},
		onLoad() {
			this.getStorageInfo()
		},
		filters:{
			formatSize(value){
												//保留1位小數 
				return value>1024? (value/1024).toFixed(1)+' MB':value.toFixed(1)+' KB';
			}
		},
		methods: {
			open(path){
				uni.navigateTo({
					url:`../${path}/${path}`
				})
			},
			getStorageInfo(){
				// 獲取緩存信息
				let res = uni.getStorageInfoSync()
				// console.log(res)
				this.currentSize = res.currentSize
			},
			// 清除緩存信息
			clear(){
				uni.showModal({
					title:'提示',
					content:'是否要清除所有緩存?',
					cancelText:'不清除',
					confirmText:'清除',
					confirmColor:'#FF4A6A',
					success: (res) => {
						if(res.confirm){
							// 清除所有本地緩存
							uni.clearStorage()
							// 更新最新的緩存信息
							this.getStorageInfo()
							// 提示一下
							uni.showToast({
								title:'清除成功',
								icon:'none'
							})
						}
					}
				})
			},
			
			// 退出登錄
			logout(){
				// 詢問是否退出登錄?
				uni.showModal({
					content:'是否要退出登錄',
					success:(res) => {
						if (res.confirm) {
							this.$store.commit('logout')
							uni.navigateBack({
								delta:1
							})
							uni.showToast({
								title:"退出登錄成功",
								icon:'none'
							})
						}
					}
				})
			}
		}
	}
</script>

<style>

</style>

Store/index.js(只看退出登錄注釋就行了)

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

Vue.use(Vuex)

export default new Vuex.Store({
	state:{
		loginStatus:false,
		user:{
	// 		"id": 400,
	// 		"username": "17621868045",
	// 		"userpic": null,
	// 		"password": false,
	// 		"phone": "17621868045",
	// 		"email": null,
	// 		"status": 1,
	// 		"create_time": 1587717384,
	// 		"logintype": "phone",
	// 		"token": "3e80c968d8b5610b9fda2341fd0ba43b39c59a40",
	// 		"userinfo": {
	// 			"id": 390,
	// 			"user_id": 400,
	// 			"age": 0,
	// 			"sex": 2,
	// 			"qg": 0,
	// 			"job": null,
	// 			"path": null,
	// 			"birthday": null
	// 		}
		}

	},
	mutations:{
		// 登錄
		login(state,user){
			// 更改state中的變量要在這里更改。
			state.loginStatus = true
			state.user = user
			// 永久存儲
			uni.setStorageSync('user',JSON.stringify(user));
		},
		
		// 初始化用戶登錄狀態
		initUser(state){
			let user = uni.getStorageSync('user');
			if (user){
				state.user = JSON.parse(user)
				state.loginStatus = true
			}
		},
		
		// 退出登錄
		logout(state){
			state.loginStatus = false
			state.user = {}
			uni.removeStorageSync('user')
			
		}	
	}
})

三 第三方登錄

效果圖:

image-20200506124333031

image-20200506124258818

思路:

以微信第三方登錄為案例:

1 點擊微信圖標

2 像微信后台發送兩次請求拿到openid和頭像等信息。

3 把拿到的信息發送到我們的后台。

4 后台存儲后發送到前台,前台把拿到的后台返回來的信息進行本地存儲。

5 然后看情況提示以及返回上一頁操作

代碼:

<template>
	<view>
		<view class="flex align-center justify-between" style="padding:20rpx 100rpx">
			<view 
			v-for="(item,index) in providerList" :key="index"
			class="iconfont text-white font-lgger rounded-circle flex align-center justify-center" 
			style="height:100rpx;width:100rpx;"
			:class="item.icon+' '+item.bgColor"
			@click="login(item)"> 
			<!-- 如果直接寫了函數就會直接調用 :login="login(item)"-->
			<!-- 完善login函數 實現第三方登錄。 -->  
				
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		props:{
			back:{
				type:Boolean,
				default:false
				
			}
		},
		data() {
			return {
				providerList: []
			}
		},
		mounted() {
			uni.getProvider({
				service: 'oauth',
				success: (result) => {
					// console.log(result.provider)
					this.providerList = result.provider.map((value) => {
						let providerName = '';
						let icon = ''
						let bgColor = ''
						switch (value) {
							case 'weixin':
								providerName = '微信登錄'
								// 自己在這個位置添加上需要渲染的信息 如 icon bgColor
								icon = 'icon-weixin'
								bgColor = 'bg-success'
								break;
							case 'qq':
								providerName = 'QQ登錄'
								icon = 'icon-QQ'
								bgColor = 'bg-primary'
								break;
							case 'sinaweibo':
								providerName = '新浪微博登錄'
								icon = 'icon-xinlangweibo'
								bgColor = 'bg-warning'
								break;
						}
						// 返回的時候
						return {
							name: providerName,
							id: value,
							icon:icon,
							bgColor:bgColor
						}
					});
		
				},
				fail: (error) => {
					console.log('獲取登錄通道失敗', error);
				}
			});
		},
		methods: {
			// 登錄
			login(item){
				console.log('###',item)
				// 微信需要先login一下,才能執行里面的uni.getUserInfo
				// 這個uni.login的工具函數,可以渲染出來微信登錄的頁面
				uni.login({
					provider:item.id,
					success:res => {
						console.log(res)
						// 獲取用戶信息
						uni.getUserInfo({
							provider:item.id,//'weixin'
						    success:(infoRes) =>{
								// console.log('####',infoRes)
								// {
								// 	"errMsg": "getUserInfo:ok",
								// 	"userInfo": {
								// 		"openId": "oRrdQtzJovVH1ZbCqvf9rQ",
								// 		"nickName": "不爭",
								// 		"gender": 1,
								// 		"city": "白山",
								// 		"province": "吉林",
								// 		"country": "中國",
								// 		"avatarUrl": "http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJYwJfvYwOp29uZAvfRoPSMvo357ibaGh79IdZ40ja3kQS9Sp7VTWhUHcg/132",
								// 		"unionId": "oU5YytxKvy2K0oUftFxmm4"
								// 	}
								// }
								let obj = {
									provider:item.id, // weixin/weibo/qq
									openid :infoRes.userInfo.openId,// 唯一id
									expires_in:0,// 過期時間
									nickName:infoRes.userInfo.nickName, // 微信的昵稱
									avatarUrl:infoRes.userInfo.avatarUrl// 微信頭像地址
								}
								console.log(obj)
								// 從微信/qq拿到的信息提交給后端。
								this.loginEvent(obj)
								
							},	
							
						})
					}
				})
			},
			
			// 拿到第三方來的登錄信息請求后端。
			loginEvent(data){
				this.$H.post('/user/otherlogin',data)
				.then(res=>{
					// 修改vuex的state,持久化存儲
					this.$store.commit('login',this.$U.formatUserinfo(res))
					
					// 返回上一頁
					if (this.back){
						uni.navigateBack({
							delta:1
						})
					}
					uni.showToast({
						title:'登錄成功',
						icon:'none'
					})	
				})
			}
		}
	}
</script>

<style>

</style>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM