mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 18:49:06 +08:00 
			
		
		
		
	增加登陆界面
This commit is contained in:
		
							
								
								
									
										47
									
								
								yudao-vue-ui/common/js/util.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								yudao-vue-ui/common/js/util.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| let _debounceTimeout = null, | ||||
| 	_throttleRunning = false | ||||
| 	 | ||||
| /** | ||||
|  * 防抖 | ||||
|  * 参考文章 https://juejin.cn/post/6844903669389885453 | ||||
|  *  | ||||
|  * @param {Function} 执行函数 | ||||
|  * @param {Number} delay 延时ms    | ||||
|  */ | ||||
| export const debounce = (fn, delay=500) => { | ||||
| 	clearTimeout(_debounceTimeout); | ||||
| 	_debounceTimeout = setTimeout(() => { | ||||
| 		fn(); | ||||
| 	}, delay); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 节流 | ||||
|  * 参考文章 https://juejin.cn/post/6844903669389885453 | ||||
|  *  | ||||
|  * @param {Function} 执行函数 | ||||
|  * @param {Number} delay 延时ms   | ||||
|  */ | ||||
| export const throttle = (fn, delay=500) => { | ||||
| 	if(_throttleRunning){ | ||||
| 		return; | ||||
| 	} | ||||
| 	_throttleRunning = true; | ||||
| 	fn(); | ||||
| 	setTimeout(() => { | ||||
| 	    _throttleRunning = false; | ||||
| 	}, delay); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * toast | ||||
|  */ | ||||
| export const msg = (title = '', param={}) => { | ||||
| 	if(!title) return; | ||||
| 	uni.showToast({ | ||||
| 		title, | ||||
| 		duration: param.duration || 1500, | ||||
| 		mask: param.mask || false, | ||||
| 		icon: param.icon || 'none' | ||||
| 	}); | ||||
| } | ||||
							
								
								
									
										96
									
								
								yudao-vue-ui/common/mixin/mixin.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								yudao-vue-ui/common/mixin/mixin.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,96 @@ | ||||
| // import {request} from '@/common/js/request' | ||||
|  | ||||
| export default{ | ||||
| 	data() { | ||||
| 		return { | ||||
| 			page: 0, // 页码 | ||||
| 			pageNum: 6, // 每页加载数据量 | ||||
| 			loadingType: 1, // 加载类型。0 加载前;1 加载中;2 没有更多 | ||||
| 			isLoading: false, // 刷新数据 | ||||
| 			loaded: false, // 加载完毕 | ||||
| 		} | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		/** | ||||
| 		 * 打印日志,方便调试 | ||||
| 		 * | ||||
| 		 * @param {Object} data 数据 | ||||
| 		 */ | ||||
| 		log(data) { | ||||
| 			console.log(JSON.parse(JSON.stringify(data))) | ||||
| 		}, | ||||
| 		 | ||||
| 		/** | ||||
| 		 * navigatorTo 跳转页面 | ||||
| 		 *  | ||||
| 		 * @param {String} url | ||||
| 		 * @param {Object} options 可选参数 | ||||
| 		 * @param {Boolean} options.login 是否检测登录   | ||||
| 		 */ | ||||
| 		navTo(url, options={}) { | ||||
| 			this.$util.throttle(() => { | ||||
| 				if (!url) { | ||||
| 					return; | ||||
| 				} | ||||
| 				// 如果需要登陆,并且未登陆,则跳转到登陆界面 | ||||
| 				if ((~url.indexOf('login=1') || options.login) && !this.$store.getters.hasLogin){ | ||||
| 					url = '/pages/auth/login'; | ||||
| 				} | ||||
| 				// 跳转到指定 url 地址 | ||||
| 				uni.navigateTo({ | ||||
| 					url | ||||
| 				}) | ||||
| 			}, 300) | ||||
| 		}, | ||||
| 		 | ||||
| 		/** | ||||
| 		 * $request云函数请求 TODO 芋艿:需要改成自己的 | ||||
| 		 * @param {String} module | ||||
| 		 * @param {String} operation | ||||
| 		 * @param {Boolean} data 请求参数 | ||||
| 		 * @param {Boolean} ext 附加参数 | ||||
| 		 * @param {Boolean} ext.showLoading 是否显示loading状态,默认不显示 | ||||
| 		 * @param {Boolean} ext.hideLoading 是否关闭loading状态,默认关闭 | ||||
| 		 * @param {Boolean} ext.login 未登录拦截 | ||||
| 		 * @param {Boolean} ext.setLoaded 加载完成是设置页面加载完毕 | ||||
| 		 */ | ||||
| 		$request(module, operation, data={}, ext={}){ | ||||
| 			if(ext.login && !this.$util.isLogin()){ | ||||
| 				return; | ||||
| 			} | ||||
| 			if(ext.showLoading){ | ||||
| 				this.isLoading = true; | ||||
| 			} | ||||
| 			return new Promise((resolve, reject)=> { | ||||
| 				request(module, operation, data, ext).then(result => { | ||||
| 					if(ext.hideLoading !== false){ | ||||
| 						this.isLoading = false; | ||||
| 					} | ||||
| 					setTimeout(()=>{ | ||||
| 						if(this.setLoaded !== false){ | ||||
| 							this.loaded = true; | ||||
| 						} | ||||
| 					}, 100) | ||||
| 					this.$refs.confirmBtn && this.$refs.confirmBtn.stop(); | ||||
| 					resolve(result); | ||||
| 				}).catch((err) => { | ||||
| 					reject(err); | ||||
| 				}) | ||||
| 			}) | ||||
| 		}, | ||||
| 		imageOnLoad(data, key){ // TODO 芋艿:需要改成自己的 | ||||
| 			setTimeout(()=>{ | ||||
| 				this.$set(data, 'loaded', true); | ||||
| 			}, 100) | ||||
| 		}, | ||||
| 		showPopup(key){ //  TODO 芋艿:需要改成自己的 | ||||
| 			this.$util.throttle(()=>{ | ||||
| 				this.$refs[key].open(); | ||||
| 			}, 200) | ||||
| 		}, | ||||
| 		hidePopup(key){ //  TODO 芋艿:需要改成自己的 | ||||
| 			this.$refs[key].close(); | ||||
| 		}, | ||||
| 		stopPrevent(){}, //  TODO 芋艿:需要改成自己的 | ||||
| 	}, | ||||
| } | ||||
| @ -1,5 +1,31 @@ | ||||
| import App from './App' | ||||
|  | ||||
| // 全局 Mixin | ||||
| import mixin from './common/mixin/mixin' | ||||
| Vue.mixin(mixin)  | ||||
|  | ||||
| // 全局 Util | ||||
| import { | ||||
| 	msg, | ||||
| 	isLogin, | ||||
| 	debounce, | ||||
| 	throttle, | ||||
| 	prePage, | ||||
| 	date | ||||
| } from '@/common/js/util' | ||||
| Vue.prototype.$util = { | ||||
| 	msg, | ||||
| 	isLogin, | ||||
| 	debounce, | ||||
| 	throttle, | ||||
| 	prePage, | ||||
| 	date | ||||
| } | ||||
|  | ||||
| // 全局 Store | ||||
| import store from './store' | ||||
| Vue.prototype.$store = store | ||||
|  | ||||
| // #ifndef VUE3 | ||||
| import Vue from 'vue' | ||||
| Vue.config.productionTip = false | ||||
|  | ||||
| @ -11,7 +11,21 @@ | ||||
| 				"navigationBarTitleText": "我的", | ||||
| 				"navigationStyle": "custom" | ||||
| 			} | ||||
| 		} | ||||
| 		}, { | ||||
| 			"path": "pages/auth/login", | ||||
| 			"style": { | ||||
| 				"navigationBarTitleText": "登录", | ||||
| 				"navigationStyle":"custom", | ||||
| 				"app-plus": { | ||||
| 					"animationType": "slide-in-bottom" | ||||
| 				} | ||||
| 			} | ||||
| 		}, { | ||||
|             "path" : "pages/set/userInfo", | ||||
|             "style" : { | ||||
| 				"navigationBarTitleText": "个人资料" | ||||
| 			} | ||||
|         } | ||||
| 	], | ||||
| 	"globalStyle": { | ||||
| 		"navigationBarTextStyle": "black", | ||||
|  | ||||
							
								
								
									
										323
									
								
								yudao-vue-ui/pages/auth/login.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										323
									
								
								yudao-vue-ui/pages/auth/login.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,323 @@ | ||||
| <template> | ||||
| 	<view class="app"> | ||||
| 		<view class="left-bottom-sign"></view> | ||||
| 		<view class="back-btn mix-icon icon-guanbi" @click="navBack"></view> | ||||
| 		<view class="right-top-sign"></view> | ||||
| 		<view class="agreement center"> | ||||
| 			<text class="mix-icon icon-xuanzhong" :class="{active: agreement}" @click="checkAgreement"></text> | ||||
| 			<text @click="checkAgreement">请认真阅读并同意</text> | ||||
| 			<text class="tit" @click="navToAgreementDetail(1)">《用户服务协议》</text> | ||||
| 			<text class="tit" @click="navToAgreementDetail(2)">《隐私权政策》</text> | ||||
| 		</view> | ||||
| 		<view class="wrapper"> | ||||
| 			<view class="left-top-sign">LOGIN</view> | ||||
| 			<view class="welcome"> | ||||
| 				欢迎回来! | ||||
| 			</view> | ||||
| 			<view class="input-content"> | ||||
| 				<view class="input-item"> | ||||
| 					<text class="tit">手机号码</text> | ||||
| 					<view class="row"> | ||||
| 						<input | ||||
| 							v-model="username" | ||||
| 							type="number"  | ||||
| 							maxlength="11" | ||||
| 							placeholder="请输入手机号码" | ||||
| 							placeholder-style="color: #909399" | ||||
| 						/> | ||||
| 					</view> | ||||
| 				</view> | ||||
| 				<view class="input-item"> | ||||
| 					<text class="tit">验证码</text> | ||||
| 					<view class="row"> | ||||
| 						<input | ||||
| 							v-model="code" | ||||
| 							type="number" | ||||
| 							maxlength="6" | ||||
| 							placeholder="请输入手机验证码" | ||||
| 							placeholder-style="color: #909399" | ||||
| 						/> | ||||
| 						<mix-code :mobile="username" templateCode="SMS_194050994"></mix-code> | ||||
| 					</view> | ||||
| 				</view> | ||||
| 			</view> | ||||
| 			<mix-button ref="confirmBtn" text="登录" marginTop="60rpx" @onConfirm="login"></mix-button> | ||||
| 			 | ||||
| 			<!-- #ifdef APP-PLUS || MP-WEIXIN --> | ||||
| 			<view class="other-wrapper"> | ||||
| 				<view class="line center"> | ||||
| 					<text class="tit">快捷登录</text> | ||||
| 				</view> | ||||
| 				<view class="list row"> | ||||
| 					<!-- #ifdef MP-WEIXIN --> | ||||
| 					<view class="item column center" @click="mpWxGetUserInfo"> | ||||
| 						<image class="icon" src="/static/icon/login-wx.png"></image> | ||||
| 					</view> | ||||
| 					<!-- #endif --> | ||||
| 					 | ||||
| 					<!-- #ifdef APP-PLUS --> | ||||
| 					<view v-if="canUseAppleLogin && false" class="item column center" style="width: 180rpx;" @click="loginByApple"> | ||||
| 						<image class="icon" src="/static/icon/apple.png"></image> | ||||
| 						<text>Apple登录</text> | ||||
| 					</view> | ||||
| 					<view class="item column center" style="width: 180rpx;" @click="loginByWxApp"> | ||||
| 						<image class="icon" src="/static/icon/login-wx.png"></image> | ||||
| 						<text>微信登录</text> | ||||
| 					</view> | ||||
| 					<!-- #endif --> | ||||
| 				</view> | ||||
| 			</view> | ||||
| 			<!-- #endif --> | ||||
| 		</view> | ||||
| 		 | ||||
| 		<mix-loading v-if="isLoading"></mix-loading> | ||||
| 	</view> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| 	import {checkStr} from '@/common/js/util' | ||||
| 	import loginMpWx from './mixin/login-mp-wx.js' | ||||
| 	import loginAppWx from './mixin/login-app-wx.js' | ||||
| 	import loginApple from './mixin/login-apple.js' | ||||
| 	export default{ | ||||
| 		mixins: [loginMpWx, loginAppWx, loginApple], | ||||
| 		data(){ | ||||
| 			return { | ||||
| 				canUseAppleLogin: false, | ||||
| 				agreement: true, | ||||
| 				username: '', | ||||
| 				code: '', | ||||
| 			} | ||||
| 		}, | ||||
| 		onLoad() { | ||||
| 			console.log(1); | ||||
| 		}, | ||||
| 		methods: { | ||||
| 			loginSuccessCallBack(data){ | ||||
| 				this.$util.msg('登陆成功'); | ||||
| 				this.$store.commit('setToken', data); | ||||
| 				setTimeout(()=>{ | ||||
| 					uni.navigateBack(); | ||||
| 				}, 1000) | ||||
| 			}, | ||||
| 			//手机号登录 | ||||
| 			async login(){ | ||||
| 				if(!this.agreement){ | ||||
| 					this.$util.msg('请阅读并同意用户服务及隐私协议'); | ||||
| 					this.$refs.confirmBtn.stop(); | ||||
| 					return; | ||||
| 				} | ||||
| 				const {username, code} = this; | ||||
| 				if(!checkStr(username, 'mobile')){ | ||||
| 					this.$util.msg('请输入正确的手机号码'); | ||||
| 					this.$refs.confirmBtn.stop(); | ||||
| 					return; | ||||
| 				} | ||||
| 				if(!checkStr(code, 'mobileCode')){ | ||||
| 					this.$util.msg('验证码错误'); | ||||
| 					this.$refs.confirmBtn.stop(); | ||||
| 					return; | ||||
| 				} | ||||
| 				const res = await this.$request('user', 'login', {username,code}); | ||||
| 				this.$refs.confirmBtn.stop(); | ||||
| 				 | ||||
| 				if(res.status === 1){ | ||||
| 					this.loginSuccessCallBack(res.data); | ||||
| 				}else{ | ||||
| 					this.$util.msg(res.msg); | ||||
| 				} | ||||
| 			}, | ||||
| 			navBack(){ | ||||
| 				uni.navigateBack(); | ||||
| 			}, | ||||
| 			//同意协议 | ||||
| 			checkAgreement(){ | ||||
| 				this.agreement = !this.agreement; | ||||
| 			}, | ||||
| 			//打开协议 | ||||
| 			navToAgreementDetail(type){ | ||||
| 				this.navTo('/pages/public/article?param=' + JSON.stringify({ | ||||
| 					module: 'article', | ||||
| 					operation: 'getAgreement', | ||||
| 					data: { | ||||
| 						type | ||||
| 					} | ||||
| 				})) | ||||
| 			}, | ||||
| 		} | ||||
| 	} | ||||
| </script> | ||||
|  | ||||
| <style> | ||||
| 	page{ | ||||
| 		background: #fff; | ||||
| 	} | ||||
| </style> | ||||
| <style scoped lang='scss'> | ||||
| 	.app{ | ||||
| 		padding-top: 15vh; | ||||
| 		position:relative; | ||||
| 		width: 100vw; | ||||
| 		height: 100vh; | ||||
| 		overflow: hidden; | ||||
| 		background: #fff; | ||||
| 	} | ||||
| 	.wrapper{ | ||||
| 		position:relative; | ||||
| 		z-index: 90; | ||||
| 		padding-bottom: 40rpx; | ||||
| 	} | ||||
| 	.back-btn{ | ||||
| 		position:absolute; | ||||
| 		left: 20rpx; | ||||
| 		top: calc(var(--status-bar-height) + 20rpx); | ||||
| 		z-index: 90; | ||||
| 		padding: 20rpx; | ||||
| 		font-size: 32rpx; | ||||
| 		color: #606266; | ||||
| 	} | ||||
| 	.left-top-sign{ | ||||
| 		font-size: 120rpx; | ||||
| 		color: #f8f8f8; | ||||
| 		position:relative; | ||||
| 		left: -12rpx; | ||||
| 	} | ||||
| 	.right-top-sign{ | ||||
| 		position:absolute; | ||||
| 		top: 80rpx; | ||||
| 		right: -30rpx; | ||||
| 		z-index: 95; | ||||
| 		 | ||||
| 		&:before, &:after{ | ||||
| 			display:block; | ||||
| 			content:""; | ||||
| 			width: 400rpx; | ||||
| 			height: 80rpx; | ||||
| 			background: #b4f3e2; | ||||
| 		} | ||||
| 		&:before{ | ||||
| 			transform: rotate(50deg); | ||||
| 			border-top-right-radius: 50px; | ||||
| 		} | ||||
| 		&:after{ | ||||
| 			position: absolute; | ||||
| 			right: -198rpx; | ||||
| 			top: 0; | ||||
| 			transform: rotate(-50deg); | ||||
| 			border-top-left-radius: 50px; | ||||
| 		} | ||||
| 	} | ||||
| 	.left-bottom-sign{ | ||||
| 		position:absolute; | ||||
| 		left: -270rpx; | ||||
| 		bottom: -320rpx; | ||||
| 		border: 100rpx solid #d0d1fd; | ||||
| 		border-radius: 50%; | ||||
| 		padding: 180rpx; | ||||
| 	} | ||||
| 	.welcome{ | ||||
| 		position:relative; | ||||
| 		left: 50rpx; | ||||
| 		top: -90rpx; | ||||
| 		font-size: 46rpx; | ||||
| 		color: #555; | ||||
| 		text-shadow: 1px 0px 1px rgba(0,0,0,.3); | ||||
| 	} | ||||
| 	.input-content{ | ||||
| 		padding: 0 60rpx; | ||||
| 	} | ||||
| 	.input-item{ | ||||
| 		display:flex; | ||||
| 		flex-direction: column; | ||||
| 		align-items:flex-start; | ||||
| 		justify-content: center; | ||||
| 		padding: 0 30rpx; | ||||
| 		background: #f8f6fc; | ||||
| 		height: 120rpx; | ||||
| 		border-radius: 4px; | ||||
| 		margin-bottom: 50rpx; | ||||
| 		 | ||||
| 		&:last-child{ | ||||
| 			margin-bottom: 0; | ||||
| 		} | ||||
| 		.row{ | ||||
| 			width: 100%; | ||||
| 		} | ||||
| 		.tit{ | ||||
| 			height: 50rpx; | ||||
| 			line-height: 56rpx; | ||||
| 			font-size: 26rpx; | ||||
| 			color: #606266; | ||||
| 		} | ||||
| 		input{ | ||||
| 			flex: 1; | ||||
| 			height: 60rpx; | ||||
| 			font-size: 30rpx; | ||||
| 			color: #303133; | ||||
| 			width: 100%; | ||||
| 		}	 | ||||
| 	} | ||||
| 	/* 其他登录方式 */ | ||||
| 	.other-wrapper{ | ||||
| 		display: flex; | ||||
| 		flex-direction: column; | ||||
| 		align-items: center; | ||||
| 		padding-top: 20rpx; | ||||
| 		margin-top: 80rpx; | ||||
| 		 | ||||
| 		.line{ | ||||
| 			margin-bottom: 40rpx; | ||||
| 			 | ||||
| 			.tit{ | ||||
| 				margin: 0 32rpx; | ||||
| 				font-size: 24rpx; | ||||
| 				color: #606266; | ||||
| 			} | ||||
| 			&:before, &:after{ | ||||
| 				content: ''; | ||||
| 				width: 160rpx; | ||||
| 				height: 0; | ||||
| 				border-top: 1px solid #e0e0e0; | ||||
| 			} | ||||
| 		} | ||||
| 		.item{ | ||||
| 			font-size: 24rpx; | ||||
| 			color: #606266; | ||||
| 			background-color: #fff; | ||||
| 			border: 0; | ||||
| 			 | ||||
| 			&:after{ | ||||
| 				border: 0; | ||||
| 			} | ||||
| 		} | ||||
| 		.icon{ | ||||
| 			width: 90rpx; | ||||
| 			height: 90rpx; | ||||
| 			margin: 0 24rpx 16rpx; | ||||
| 		} | ||||
| 	} | ||||
| 	.agreement{ | ||||
| 		position: absolute; | ||||
| 		left: 0; | ||||
| 		bottom: 6vh; | ||||
| 		z-index: 1; | ||||
| 		width: 750rpx; | ||||
| 		height: 90rpx; | ||||
| 		font-size: 24rpx; | ||||
| 		color: #999; | ||||
| 		 | ||||
| 		.mix-icon{ | ||||
| 			font-size: 36rpx; | ||||
| 			color: #ccc; | ||||
| 			margin-right: 8rpx; | ||||
| 			margin-top: 1px; | ||||
| 			 | ||||
| 			&.active{ | ||||
| 				color: $base-color; | ||||
| 			} | ||||
| 		} | ||||
| 		.tit{ | ||||
| 			color: #40a2ff; | ||||
| 		} | ||||
| 	} | ||||
| </style> | ||||
							
								
								
									
										73
									
								
								yudao-vue-ui/pages/auth/mixin/login-app-wx.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								yudao-vue-ui/pages/auth/mixin/login-app-wx.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| export default{ | ||||
| 	// #ifdef APP-PLUS | ||||
| 	methods: { | ||||
| 		/** | ||||
| 		 * 微信App登录 | ||||
| 		 *  "openId": "o0yywwGWxtBCvBuE8vH4Naof0cqU", | ||||
| 		 *	"nickName": "S .", | ||||
| 		 *	"gender": 1, | ||||
| 		 *	"city": "临沂", | ||||
| 		 *	"province": "山东", | ||||
| 		 *	"country": "中国", | ||||
| 		 *	"avatarUrl": "http://thirdwx.qlogo.cn/mmopen/vi_32/xqpCtHRBBmdlf201Fykhtx7P7JcicIbgV3Weic1oOvN6iaR3tEbuu74f2fkKQWXvzK3VDgNTZzgf0g8FqPvq8LCNQ/132", | ||||
| 		 *	"unionId": "oYqy4wmMcs78x9P-tsyMeM3MQ1PU" | ||||
| 		 */ | ||||
| 		loginByWxApp(userInfoData){ | ||||
| 			if(!this.agreement){ | ||||
| 				this.$util.msg('请阅读并同意用户服务及隐私协议'); | ||||
| 				return; | ||||
| 			} | ||||
| 			this.$util.throttle(async ()=>{ | ||||
| 				let [err, res] = await uni.login({ | ||||
| 					provider: 'weixin' | ||||
| 				}) | ||||
| 				if(err){ | ||||
| 					console.log(err); | ||||
| 					return; | ||||
| 				} | ||||
| 				uni.getUserInfo({ | ||||
| 					provider: 'weixin', | ||||
| 					success: async res=>{ | ||||
| 						const response = await this.$request('user', 'loginByWeixin', { | ||||
| 							userInfo: res.userInfo, | ||||
| 						}, { | ||||
| 							showLoading: true | ||||
| 						}); | ||||
| 						if(response.status === 0){ | ||||
| 							this.$util.msg(response.msg); | ||||
| 							return; | ||||
| 						} | ||||
| 						if(response.hasBindMobile && response.data.token){ | ||||
| 							this.loginSuccessCallBack({ | ||||
| 								token: response.data.token, | ||||
| 								tokenExpired: response.data.tokenExpired | ||||
| 							}); | ||||
| 						}else{ | ||||
| 							this.navTo('/pages/auth/bindMobile?data='+JSON.stringify(response.data)) | ||||
| 						} | ||||
| 						plus.oauth.getServices(oauthRes=>{ | ||||
| 							oauthRes[0].logout(logoutRes => { | ||||
| 								console.log(logoutRes); | ||||
| 							}, error => { | ||||
| 								console.log(error); | ||||
| 							}) | ||||
| 						}) | ||||
| 					}, | ||||
| 					fail(err) { | ||||
| 						console.log(err); | ||||
| 					} | ||||
| 				}) | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
| 	// #endif | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										83
									
								
								yudao-vue-ui/pages/auth/mixin/login-apple.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								yudao-vue-ui/pages/auth/mixin/login-apple.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| export default{ | ||||
| 	onLoad() { | ||||
| 		if(this.systemInfo.platform !== 'ios'){ | ||||
| 			return; | ||||
| 		} | ||||
| 		const systemVersion = +this.systemInfo.system.split('.')[0]; | ||||
| 		if(systemVersion >= 13){ | ||||
| 			this.canUseAppleLogin = true; | ||||
| 		} | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		//苹果登录 | ||||
| 		async loginByApple(){ | ||||
| 			/* if(!this.canUseAppleLogin){ | ||||
| 				this.$util.msg('系统版本过低,无法使用苹果登录'); | ||||
| 				return; | ||||
| 			} */  | ||||
| 			if(!this.agreement){ | ||||
| 				this.$util.msg('请阅读并同意用户服务及隐私协议'); | ||||
| 				this.$refs.confirmBtn.stop(); | ||||
| 				return; | ||||
| 			} | ||||
| 			uni.login({   | ||||
| 			    provider: 'apple',   | ||||
| 			    success: loginRes=> {   | ||||
| 			        // 登录成功    | ||||
| 			        uni.getUserInfo({   | ||||
| 			            provider: 'apple',   | ||||
| 			            success: async userRes=> { | ||||
| 							console.log(userRes); | ||||
| 							const response = await this.$request('user', 'loginByApple', { | ||||
| 								authorizationCode: userRes.userInfo.authorizationCode, | ||||
| 								identityToken: userRes.userInfo.identityToken | ||||
| 							}, { | ||||
| 								showLoading: true | ||||
| 							}); | ||||
| 							console.log(response); | ||||
| 							//注销苹果登录 | ||||
| 							this.appleLogout(); | ||||
| 							console.log(response); | ||||
| 							if(response.status === 0){ | ||||
| 								this.$util.msg(response.msg); | ||||
| 								return; | ||||
| 							} | ||||
| 							if(response.hasBindMobile && response.data.token){ | ||||
| 								this.loginSuccessCallBack({ | ||||
| 									token: response.data.token, | ||||
| 									tokenExpired: response.data.tokenExpired | ||||
| 								}); | ||||
| 							}else{ | ||||
| 								this.navTo('/pages/auth/bindMobile?type=apple&data='+JSON.stringify(response.data)) | ||||
| 							} | ||||
| 			            }   | ||||
| 			        })   | ||||
| 			    },   | ||||
| 			    fail: err=> {   | ||||
| 					console.log(err); | ||||
| 					this.$util.msg('登录失败'); | ||||
| 					this.appleLogout(); | ||||
| 			    }   | ||||
| 			}) | ||||
| 		}, | ||||
| 		appleLogout(){ | ||||
| 			plus.oauth.getServices(oauthRes=>{ | ||||
| 				const oIndex = oauthRes.findIndex(oItem=> oItem.id === 'apple'); | ||||
| 				oauthRes[oIndex].logout(loRes => { | ||||
| 					console.log('appleLogout success=> ', loRes); | ||||
| 				}, loErr => { | ||||
| 					console.log('appleLogout error=> ', loErr); | ||||
| 				}) | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										81
									
								
								yudao-vue-ui/pages/auth/mixin/login-mp-wx.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								yudao-vue-ui/pages/auth/mixin/login-mp-wx.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| export default{ | ||||
| 	// #ifdef MP-WEIXIN | ||||
| 	data(){ | ||||
| 		return { | ||||
| 			mpCodeTimer: 0, | ||||
| 			mpWxCode: '', | ||||
| 		} | ||||
| 	}, | ||||
| 	computed: { | ||||
| 		timerIdent(){ | ||||
| 			return this.$store.state.timerIdent; | ||||
| 		} | ||||
| 	}, | ||||
| 	watch: { | ||||
| 		timerIdent(){ | ||||
| 			this.mpCodeTimer ++; | ||||
| 			if(this.mpCodeTimer % 30 === 0){ | ||||
| 				this.getMpWxCode(); | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| 	onShow(){ | ||||
| 		this.getMpWxCode(); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		//微信小程序登录 | ||||
| 		mpWxGetUserInfo(){ | ||||
| 			if(!this.agreement){ | ||||
| 				this.$util.msg('请阅读并同意用户服务及隐私协议'); | ||||
| 				return; | ||||
| 			} | ||||
| 			 | ||||
| 			this.$util.throttle(()=>{ | ||||
| 				uni.getUserProfile({ | ||||
| 					desc: '用于展示您的头像及昵称', | ||||
| 					success: async profileRes=> { | ||||
| 						const res = await this.$request('user', 'loginByWeixin', { | ||||
| 							code: this.mpWxCode, | ||||
| 							...profileRes.userInfo | ||||
| 						}, { | ||||
| 							showLoading: true | ||||
| 						}); | ||||
| 						if(res.status === 0){ | ||||
| 							this.$util.msg(res.msg); | ||||
| 							return; | ||||
| 						} | ||||
| 						if(res.hasBindMobile && res.data.token){ | ||||
| 							this.loginSuccessCallBack({ | ||||
| 								token: res.data.token, | ||||
| 								tokenExpired: res.data.tokenExpired | ||||
| 							}); | ||||
| 						}else{ | ||||
| 							this.navTo('/pages/auth/bindMobile?data='+JSON.stringify(res.data)) | ||||
| 						} | ||||
| 						console.log(res) | ||||
| 					} | ||||
| 				}) | ||||
| 			}) | ||||
| 		}, | ||||
| 		//获取code | ||||
| 		getMpWxCode(){ | ||||
| 			uni.login({ | ||||
| 				provider: 'weixin', | ||||
| 				success: res=> { | ||||
| 					this.mpWxCode = res.code; | ||||
| 				} | ||||
| 			}) | ||||
| 		}, | ||||
| 		 | ||||
| 	} | ||||
| 	// #endif | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										248
									
								
								yudao-vue-ui/pages/set/userInfo.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								yudao-vue-ui/pages/set/userInfo.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,248 @@ | ||||
| <template> | ||||
| 	<view class="app"> | ||||
| 		<view class="cell"> | ||||
| 			<text class="tit fill">头像</text> | ||||
| 			<view class="avatar-wrap" @click="chooseImage"> | ||||
| 				<image class="avatar" :src="tempAvatar || userInfo.avatar || '/static/icon/default-avatar.png'" mode="aspectFill"></image> | ||||
| 				<!-- 进度遮盖 --> | ||||
| 				<view  | ||||
| 					class="progress center" | ||||
| 					:class="{ | ||||
| 						'no-transtion': uploadProgress === 0, | ||||
| 						show: uploadProgress != 100 | ||||
| 					}" | ||||
| 					:style="{ | ||||
| 						width: uploadProgress + '%', | ||||
| 						height: uploadProgress + '%', | ||||
| 					}" | ||||
| 				></view> | ||||
| 			</view> | ||||
| 		</view> | ||||
| 		<view class="cell b-b"> | ||||
| 			<text class="tit fill">昵称</text> | ||||
| 			<input class="input" v-model="userInfo.nickname" type="text" maxlength="8" placeholder="请输入昵称" placeholder-class="placeholder"> | ||||
| 		</view> | ||||
| 		<view class="cell b-b"> | ||||
| 			<text class="tit fill">性别</text> | ||||
| 			<view class="checkbox center" @click="changeGender(1)"> | ||||
| 				<text v-if="userInfo.gender == 1" class="mix-icon icon-xuanzhong"></text> | ||||
| 				<text v-else class="mix-icon icon-yk_yuanquan"></text> | ||||
| 				<text>男</text> | ||||
| 			</view> | ||||
| 			<view class="checkbox center" @click="changeGender(2)"> | ||||
| 				<text v-if="userInfo.gender == 2" class="mix-icon icon-xuanzhong"></text> | ||||
| 				<text v-else class="mix-icon icon-yk_yuanquan"></text> | ||||
| 				<text>女</text> | ||||
| 			</view> | ||||
| 		</view> | ||||
| 		 | ||||
| 		<mix-button ref="confirmBtn" text="保存资料" marginTop="80rpx" @onConfirm="confirm"></mix-button> | ||||
| 	</view> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| 	export default { | ||||
| 		data() { | ||||
| 			return { | ||||
| 				uploadProgress: 100, //头像上传进度 | ||||
| 				tempAvatar: '',  | ||||
| 				userInfo: {}, | ||||
| 			} | ||||
| 		}, | ||||
| 		computed: { | ||||
| 			curUserInfo(){ | ||||
| 				return this.$store.state.userInfo | ||||
| 			} | ||||
| 		}, | ||||
| 		watch: { | ||||
| 			curUserInfo(curUserInfo){ | ||||
| 				const {avatar, nickname, gender} = curUserInfo; | ||||
| 				this.userInfo = {avatar, nickname, gender,}; | ||||
| 			} | ||||
| 		}, | ||||
| 		onLoad() { | ||||
| 			const {avatar, nickname, gender, anonymous} = this.curUserInfo; | ||||
| 			this.userInfo = {avatar, nickname, gender}; | ||||
| 		}, | ||||
| 		methods: { | ||||
| 			//提交修改 | ||||
| 			async confirm(){ | ||||
| 				const {uploadProgress, userInfo, curUserInfo} = this; | ||||
| 				let isUpdate = false; | ||||
| 				for(let key in userInfo){ | ||||
| 					if(userInfo[key] !== curUserInfo[key]){ | ||||
| 						isUpdate = true; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				if (isUpdate === false) { | ||||
| 					this.$util.msg('信息未修改'); | ||||
| 					this.$refs.confirmBtn.stop(); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (!userInfo.avatar) { | ||||
| 					this.$util.msg('请上传头像'); | ||||
| 					this.$refs.confirmBtn.stop(); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (uploadProgress !== 100) { | ||||
| 					this.$util.msg('请等待头像上传完毕'); | ||||
| 					this.$refs.confirmBtn.stop(); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (!userInfo.nickname) { | ||||
| 					this.$util.msg('请输入您的昵称'); | ||||
| 					this.$refs.confirmBtn.stop(); | ||||
| 					return; | ||||
| 				} | ||||
| 				if (!userInfo.gender) { | ||||
| 					this.$util.msg('请选择您的性别'); | ||||
| 					this.$refs.confirmBtn.stop(); | ||||
| 					return; | ||||
| 				} | ||||
| 				const res = await this.$request('user', 'update', userInfo); | ||||
| 				this.$refs.confirmBtn.stop(); | ||||
| 				this.$util.msg(res.msg); | ||||
| 				if(res.status === 1){ | ||||
| 					this.$store.dispatch('getUserInfo'); //刷新用户信息 | ||||
| 					setTimeout(()=>{ | ||||
| 						uni.navigateBack(); | ||||
| 					}, 1000) | ||||
| 				} | ||||
| 			}, | ||||
| 			//选择头像 | ||||
| 			chooseImage(){ | ||||
| 				uni.chooseImage({ | ||||
| 					count: 1, | ||||
| 					success: res=> { | ||||
| 						uni.navigateTo({ | ||||
| 							url: `./cutImage/cut?src=${res.tempFilePaths[0]}`  | ||||
| 						}); | ||||
| 					} | ||||
| 				}); | ||||
| 			},  | ||||
| 			//裁剪回调 | ||||
| 			async setAvatar(filePath){ | ||||
| 				this.tempAvatar = filePath; | ||||
| 				this.uploadProgress = 0; | ||||
| 				const result = await uniCloud.uploadFile({ | ||||
| 					filePath: filePath, | ||||
| 					cloudPath: + new Date() + ('000000' + Math.floor(Math.random() * 999999)).slice(-6) + '.jpg', | ||||
| 					onUploadProgress: e=> { | ||||
| 						this.uploadProgress = Math.round( | ||||
| 							(e.loaded * 100) / e.total | ||||
| 						); | ||||
| 					} | ||||
| 				}); | ||||
| 				if(!result.fileID){ | ||||
| 					this.$util.msg('头像上传失败'); | ||||
| 					return; | ||||
| 				} | ||||
| 				if(typeof uniCloud.getTempFileURL === 'undefined'){ | ||||
| 					this.userInfo.avatar = result.fileID; | ||||
| 				}else{ | ||||
| 					const tempFiles = await uniCloud.getTempFileURL({ | ||||
| 						fileList: [result.fileID] | ||||
| 					}) | ||||
| 					const tempFile = tempFiles.fileList[0]; | ||||
| 					if(tempFile.download_url || tempFile.fileID){ | ||||
| 						this.userInfo.avatar = tempFile.download_url || tempFile.fileID; | ||||
| 					}else{ | ||||
| 						this.$util.msg('头像上传失败'); | ||||
| 					} | ||||
| 				} | ||||
| 			}, | ||||
| 			//修改性别 | ||||
| 			changeGender(gender){ | ||||
| 				this.$set(this.userInfo, 'gender', gender) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="scss"> | ||||
| 	.app{ | ||||
| 		padding-top: 16rpx; | ||||
| 	} | ||||
| 	.cell{ | ||||
| 		display: flex; | ||||
| 		align-items: center; | ||||
| 		min-height: 110rpx; | ||||
| 		padding: 0 40rpx; | ||||
| 		 | ||||
| 		&:first-child{ | ||||
| 			margin-bottom: 10rpx; | ||||
| 		} | ||||
| 		&:after{ | ||||
| 			left: 40rpx; | ||||
| 			right: 40rpx; | ||||
| 			border-color: #d8d8d8; | ||||
| 		} | ||||
| 		.tit{ | ||||
| 			font-size: 30rpx; | ||||
| 			color: #333; | ||||
| 		} | ||||
| 		.avatar-wrap{ | ||||
| 			width: 120rpx; | ||||
| 			height: 120rpx; | ||||
| 			position: relative; | ||||
| 			border-radius: 100rpx; | ||||
| 			overflow: hidden; | ||||
| 			 | ||||
| 			.avatar{ | ||||
| 				width: 100%; | ||||
| 				height: 100%; | ||||
| 				border-radius: 100rpx; | ||||
| 			} | ||||
| 			.progress{ | ||||
| 				position: absolute; | ||||
| 				left: 50%; | ||||
| 				top: 50%; | ||||
| 				transform: translate(-50%, -50%); | ||||
| 				width: 100rpx; | ||||
| 				height: 100rpx; | ||||
| 				box-shadow: rgba(0,0,0,.6) 0px 0px 0px 2005px; | ||||
| 				border-radius: 100rpx; | ||||
| 				transition: .5s; | ||||
| 				opacity: 0; | ||||
| 				 | ||||
| 				&.no-transtion{ | ||||
| 					transition: 0s; | ||||
| 				} | ||||
| 				&.show{ | ||||
| 					opacity: 1; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		.input{ | ||||
| 			flex: 1; | ||||
| 			text-align: right; | ||||
| 			font-size: 28rpx; | ||||
| 			color: #333; | ||||
| 		} | ||||
| 		switch{ | ||||
| 			margin: 0; | ||||
| 			transform: scale(0.8) translateX(10rpx); | ||||
| 			transform-origin: center right; | ||||
| 		} | ||||
| 		.tip{ | ||||
| 			margin-left: 20rpx; | ||||
| 			font-size: 28rpx; | ||||
| 			color: #999; | ||||
| 		} | ||||
| 		.checkbox{ | ||||
| 			padding: 12rpx 0 12rpx 40rpx; | ||||
| 			font-size: 28rpx; | ||||
| 			color: #333; | ||||
| 			 | ||||
| 			.mix-icon{ | ||||
| 				margin-right: 12rpx; | ||||
| 				font-size: 36rpx; | ||||
| 				color: #ccc; | ||||
| 			} | ||||
| 			.icon-xuanzhong{ | ||||
| 				color: $base-color; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| </style> | ||||
| @ -59,7 +59,7 @@ | ||||
| 			<!-- <mix-list-cell icon="icon-shoucang_xuanzhongzhuangtai" iconColor="#54b4ef" title="我的收藏" @onClick="navTo('/pages/favorite/favorite', {login: true})"></mix-list-cell> --> | ||||
| 			<!-- <mix-list-cell icon="icon-pinglun-copy" iconColor="#ee883b" title="意见反馈" @onClick="navTo('/pages/feedback/feedback', {login: true})"></mix-list-cell> --> | ||||
| 			<!-- <mix-list-cell icon="icon-shezhi1" iconColor="#37b0fb" title="设置" border="" @onClick="navTo('/pages/set/set', {login: true})"></mix-list-cell> --> | ||||
| 			<mix-list-cell icon="icon-bianji" iconColor="#5fcda2" title="个人信息" border="" @onClick="navTo('/pages/set/set', {login: true})"></mix-list-cell> | ||||
| 			<mix-list-cell icon="icon-bianji" iconColor="#5fcda2" title="个人信息" border="" @onClick="navTo('/pages/set/userInfo', {login: true})"></mix-list-cell> | ||||
| 			<mix-list-cell icon="icon-shezhi1" iconColor="#37b0fb" title="账号安全" border="" @onClick="navTo('/pages/set/set', {login: true})"></mix-list-cell> | ||||
| 		</view> | ||||
| 	</view> | ||||
|  | ||||
							
								
								
									
										93
									
								
								yudao-vue-ui/store/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								yudao-vue-ui/store/index.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| import Vue from 'vue' | ||||
| import Vuex from 'vuex' | ||||
| // import {request} from '@/common/js/request' | ||||
|  | ||||
| Vue.use(Vuex) | ||||
|  | ||||
| const store = new Vuex.Store({ | ||||
| 	state: { | ||||
| 		openExamine: false, // 是否开启审核状态。用于小程序、App 等审核时,关闭部分功能。TODO 芋艿:暂时没找到刷新的地方 | ||||
| 		token: '', // 用户身份 Token | ||||
| 		userInfo: {}, // 用户基本信息 | ||||
| 		timerIdent: false, // 全局 1s 定时器,只在全局开启一个,所有需要定时执行的任务监听该值即可,无需额外开启 TODO 芋艿:需要看看 | ||||
| 		orderCount: {}, // 订单数量 | ||||
| 	}, | ||||
| 	getters: { | ||||
| 		hasLogin(state){ | ||||
| 			return !!state.token; | ||||
| 		} | ||||
| 	}, | ||||
| 	mutations: { | ||||
| 		//更新state数据 | ||||
| 		setStateAttr(state, param){ | ||||
| 			if(param instanceof Array){ | ||||
| 				for(let item of param){ | ||||
| 					state[item.key] = item.val; | ||||
| 				} | ||||
| 			}else{ | ||||
| 				state[param.key] = param.val; | ||||
| 			} | ||||
| 		}, | ||||
| 		//更新token | ||||
| 		setToken(state, data){ | ||||
| 			const {token, tokenExpired} = data; | ||||
| 			state.token = token; | ||||
| 			uni.setStorageSync('uniIdToken', token); | ||||
| 			uni.setStorageSync('tokenExpired', tokenExpired); | ||||
| 			this.dispatch('getUserInfo'); //更新用户信息 | ||||
| 			this.dispatch('getCartCount');//更新购物车数量 | ||||
| 			uni.$emit('refreshCart');//刷新购物车 | ||||
| 			this.dispatch('getOrderCount'); //更新订单数量 | ||||
| 		}, | ||||
| 		// 退出登录 | ||||
| 		logout(state) { | ||||
| 			state.token = ''; | ||||
| 			uni.removeStorageSync('uniIdToken'); | ||||
| 			this.dispatch('getCartCount');//更新购物车数量 | ||||
| 			uni.$emit('refreshCart');//刷新购物车 | ||||
| 			this.dispatch('getOrderCount'); //更新订单数量 | ||||
| 			setTimeout(()=>{ | ||||
| 				state.userInfo = {}; | ||||
| 			}, 1100) | ||||
| 		}, | ||||
| 	}, | ||||
| 	actions: { | ||||
| 		//更新用户信息 | ||||
| 		async getUserInfo({state, commit}){ | ||||
| 			const res = await request('user', 'get', {}, { | ||||
| 				checkAuthInvalid: false | ||||
| 			}); | ||||
| 			if(res.status === 1){ | ||||
| 				const userInfo = res.data; | ||||
| 				commit('setStateAttr', { | ||||
| 					key: 'userInfo', | ||||
| 					val: userInfo | ||||
| 				}) | ||||
| 			} | ||||
| 		}, | ||||
| 		//更新用户订单数量 | ||||
| 		async getOrderCount({state, commit}){ | ||||
| 			let data = { | ||||
| 				c0: 0, | ||||
| 				c1: 0, | ||||
| 				c2: 0, | ||||
| 				c3: 0 | ||||
| 			} | ||||
| 			if(state.token){ | ||||
| 				try { | ||||
| 					const res = await request('order', 'getOrderCount'); | ||||
| 					data = res; | ||||
| 				}catch (err){ | ||||
| 					console.error('更新用户订单数量 => ', err); | ||||
| 				} | ||||
| 			} | ||||
| 			commit('setStateAttr', { | ||||
| 				key: 'orderCount', | ||||
| 				val: data | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
| })  | ||||
|  | ||||
|  | ||||
| export default store | ||||
		Reference in New Issue
	
	Block a user
	 YunaiV
					YunaiV