shish 1 місяць тому
батько
коміт
956dd4b5fd

+ 5 - 60
ghsApp/src/admin/home/login.vue

@@ -187,21 +187,9 @@
 		</view>
 
 		<!-- #ifdef APP-PLUS -->
-		<web-view
-			v-if="showAppCaptcha"
-			:src="'/hybrid/html/captcha.html?sceneId=' + captchaSceneId"
-			@message="onAppCaptchaMessage"
-			style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; z-index: 99999;"
-		></web-view>
 		<!-- #endif -->
 
 		<!-- #ifdef MP-WEIXIN -->
-		<aliyun-captcha
-			v-if="showWxCaptcha"
-			:sceneId="captchaSceneId"
-			@success="onWxCaptchaSuccess"
-			@fail="onWxCaptchaFail"
-		></aliyun-captcha>
 		<!-- #endif -->
 	</view>
 </template>
@@ -224,9 +212,6 @@ export default {
 			agree:0,
 			showAccountLogin:false,
 			showSmsLogin:false,
-			showAppCaptcha: false,
-			showWxCaptcha: false,
-			captchaSceneId: 'YOUR_SCENE_ID', // 阿里云控制台申请的场景ID
 			smsCode:'',
 			smsCountdown: 0,
 			smsTimer: null,
@@ -238,6 +223,7 @@ export default {
 	onLoad(){
 		this.setStatusBarHeight()
 		this.initSmsCountdown()
+
 		let that = this
 		uni.getNetworkType({
 			success: function (res) {
@@ -332,7 +318,7 @@ export default {
 				}
 			}, 1000)
 		},
-		// 获取短信验证码(先触发人机验证)
+		// 获取短信验证码
 		getSmsCode() {
 			let mobile = this.mobile
 			if (this.$util.isEmpty(mobile)) {
@@ -344,32 +330,13 @@ export default {
 				return false
 			}
 			
-			// 触发人机验证
-			this.triggerCaptcha()
-		},
-		// 唤起人机验证组件
-		triggerCaptcha() {
-			// #ifdef APP-PLUS
-			this.showAppCaptcha = true
-			// #endif
-
-			// #ifdef MP-WEIXIN
-			this.showWxCaptcha = true
-			// #endif
-
-			// #ifndef APP-PLUS || MP-WEIXIN
-			// 其他平台(如H5或调试环境)若未集成,可直接调用发送接口
-			this.sendSmsWithCaptcha('')
-			// #endif
+			this.sendSms()
 		},
-		// 携带人机验证凭证请求发送短信
-		sendSmsWithCaptcha(captchaVerifyParam) {
-			let that = this
+		sendSms() {
 			let mobile = this.mobile
 			uni.showLoading({ mask: true })
 			sendLoginSms({ 
-				mobile: mobile,
-				captchaVerifyParam: captchaVerifyParam
+				mobile: mobile
 			}).then(res => {
 				uni.hideLoading()
 				if (res.code == 1) {
@@ -381,28 +348,6 @@ export default {
 				uni.hideLoading()
 			})
 		},
-		// App 端 WebView 消息监听
-		onAppCaptchaMessage(e) {
-			const data = e.detail.data[0]
-			if (data && data.action === 'captcha_success') {
-				this.showAppCaptcha = false
-				this.sendSmsWithCaptcha(data.captchaVerifyParam)
-			} else if (data && data.action === 'captcha_cancel') {
-				this.showAppCaptcha = false
-				this.$msg('验证已取消')
-			}
-		},
-		// 微信小程序端人机验证成功
-		onWxCaptchaSuccess(e) {
-			this.showWxCaptcha = false
-			const param = e.detail || e
-			this.sendSmsWithCaptcha(param)
-		},
-		// 微信小程序端人机验证失败
-		onWxCaptchaFail(e) {
-			this.showWxCaptcha = false
-			this.$msg('验证失败,请重试')
-		},
 		// 短信验证码登录提交
 		smsLoginFun() {
 			let that = this

+ 227 - 5
hdApp/src/admin/home/login.vue

@@ -49,8 +49,55 @@
 					</view>
 				</template>
 
+				<template v-if="showSmsLogin">
+					<view class="login-form">
+						<view class="login-field">
+							<input
+								@focus="mobile=''"
+								v-model="mobile"
+								class="login-input"
+								placeholder="请输入手机号"
+								type="number"
+							/>
+						</view>
+						<view class="login-field login-field--last" style="display: flex; align-items: center; justify-content: space-between;">
+							<view style="flex: 1; margin-right: 20upx;">
+								<input
+									@focus="smsCode=''"
+									v-model="smsCode"
+									class="login-input"
+									placeholder="请输入验证码"
+									type="number"
+								/>
+							</view>
+							<button 
+								class="admin-button-com middle blue" 
+								:disabled="smsCountdown > 0" 
+								@click="getSmsCode"
+								style="margin-top: 0; font-size: 26upx; height: 88upx; line-height: 88upx; padding: 0 20upx; min-width: 180upx; flex-shrink: 0; border-radius: 16upx;"
+							>
+								{{ smsCountdown > 0 ? smsCountdown + 's' : '获取验证码' }}
+							</button>
+						</view>
+						<button class="login-btn login-btn--primary" @click="smsLoginFun">登录</button>
+					</view>
+					<view class="login-agreement">
+						<checkbox-group @change="agreeItem">
+							<label class="login-agreement-label">
+								<checkbox value="1" class="login-checkbox" :checked="agree == 1" />
+								<text class="login-agreement-text">
+									我已阅读并同意
+									<text class="login-agreement-link" @click.stop="goToService">《服务协议》</text>
+									和
+									<text class="login-agreement-link" @click.stop="goToPrivacy">《隐私政策》</text>
+								</text>
+							</label>
+						</checkbox-group>
+					</view>
+				</template>
+
 				<!-- #ifdef MP-WEIXIN -->
-				<view v-if="showAccountLogin == false" class="login-quick">
+				<view v-if="showAccountLogin == false && showSmsLogin == false" class="login-quick">
 					<view class="login-tip">
 						<text class="login-tip-line">这是花店管理后台,需获取你的手机号登录后</text>
 						<text class="login-tip-line">才能使用相应功能,请谅解</text>
@@ -68,7 +115,7 @@
 				<!-- #endif -->
 
 				<!-- #ifdef APP-PLUS -->
-				<view v-if="showAccountLogin == false" class="login-quick">
+				<view v-if="showAccountLogin == false && showSmsLogin == false" class="login-quick">
 					<view class="login-tip">
 						<text class="login-tip-line">使用本机号码快速登录花掌柜</text>
 					</view>
@@ -90,16 +137,35 @@
 				<!-- #endif -->
 
 				<view class="login-footer-links">
+					<!-- 如果当前不是账号密码登录,显示切换到账号密码登录的链接 -->
 					<text
 						v-if="showAccountLogin == false"
 						class="login-footer-link"
 						@click="useAccount()"
 					>使用账号密码登录</text>
+					<!-- 如果当前是账号密码登录,显示切换到快捷登录的链接 -->
+					<text
+						v-else
+						class="login-footer-link"
+						@click="useMoibleAccount()"
+					>使用快捷登录</text>
+
+					<text class="login-footer-divider">|</text>
+
+					<!-- 如果当前不是短信登录,显示切换到短信登录的链接 -->
+					<text
+						v-if="showSmsLogin == false"
+						class="login-footer-link"
+						@click="useSms()"
+					>使用短信登录</text>
+					<!-- 如果当前是短信登录,显示切换到快捷登录的链接 -->
 					<text
 						v-else
 						class="login-footer-link"
 						@click="useMoibleAccount()"
-					>使用手机号登录</text>
+					>使用快捷登录</text>
+
+					<text class="login-footer-divider">|</text>
 					<text class="login-footer-link login-footer-link--register" @click="register">注册账号</text>
 				</view>
 			</view>
@@ -109,7 +175,7 @@
 <script>
 import { mapActions } from "vuex";
 import { getShopInfo} from "@/utils/auth";
-import {appLogin} from '@/api/common'
+import {appLogin, sendLoginSms, smsLogin} from '@/api/common'
 import { getDictionaries,postWxCode } from '@/api/mini'
 import { phoneLogin,wxMobileLogin } from "@/api/auth";
 import { ghsInfo } from "@/api/ghs/index";
@@ -122,11 +188,16 @@ export default {
 			password:'',
 			agree:0,
 			showAccountLogin:false,
+			showSmsLogin:false,
+			smsCode:'',
+			smsCountdown: 0,
+			smsTimer: null,
 			statusBarHeight: 0
 		};
 	},
 	onLoad(){
 		this.setStatusBarHeight();
+		this.initSmsCountdown();
 		let that = this
 		uni.getNetworkType({
 			success: function (res) {
@@ -161,6 +232,9 @@ export default {
 	},
 	onUnload(){
 		uni.offNetworkStatusChange(this.dealLogin)
+		if (this.smsTimer) {
+			clearInterval(this.smsTimer)
+		}
 	},
 	methods: {
 		...mapActions(['setUserShopAll']),
@@ -184,11 +258,19 @@ export default {
 		},
 		useMoibleAccount(){
 			this.showAccountLogin = false
+			this.showSmsLogin = false
 			this.agree = 0
 			this.$forceUpdate()
 		},
 		useAccount(){
 			this.showAccountLogin = true
+			this.showSmsLogin = false
+			this.agree = 0
+			this.$forceUpdate()
+		},
+		useSms(){
+			this.showAccountLogin = false
+			this.showSmsLogin = true
 			this.agree = 0
 			this.$forceUpdate()
 		},
@@ -490,6 +572,140 @@ export default {
 				})
 			})
 
+		},
+		/**
+		 * 【用途】初始化短信验证码倒计时
+		 * 【为什么】在页面加载时检查本地缓存的上次验证码发送时间,若在60秒内,则恢复倒计时,防止刷新页面绕过倒计时限制
+		 */
+		initSmsCountdown() {
+			let lastSentTime = uni.getStorageSync('last_sms_sent_time_login_hd')
+			if (lastSentTime) {
+				let timePassed = Math.floor((Date.now() - lastSentTime) / 1000)
+				if (timePassed < 60) {
+					this.startCountdown(60 - timePassed)
+				}
+			}
+		},
+		/**
+		 * 【用途】启动短信倒计时定时器
+		 * 【为什么】控制“获取验证码”按钮的禁用状态与倒计时秒数显示,防止频繁请求
+		 * @param {Number} seconds 倒计时秒数
+		 */
+		startCountdown(seconds) {
+			this.smsCountdown = seconds
+			if (this.smsTimer) {
+				clearInterval(this.smsTimer)
+			}
+			this.smsTimer = setInterval(() => {
+				this.smsCountdown--
+				if (this.smsCountdown <= 0) {
+					clearInterval(this.smsTimer)
+					this.smsTimer = null
+				}
+			}, 1000)
+		},
+		/**
+		 * 【用途】获取短信验证码的前置校验
+		 * 【为什么】在发送验证码前校验手机号格式,确保输入有效,校验通过后触发发送逻辑
+		 */
+		getSmsCode() {
+			let mobile = this.mobile
+			if (this.$util.isEmpty(mobile)) {
+				this.$msg('请输入手机号')
+				return false
+			}
+			if (this.$util.checkMobile(mobile) == false) {
+				this.$msg('请输入正确手机号')
+				return false
+			}
+			this.sendSms()
+		},
+		/**
+		 * 【用途】发送登录短信验证码
+		 * 【为什么】调用后端发送短信接口,成功后记录发送时间戳并开启60秒倒计时
+		 */
+		sendSms() {
+			let mobile = this.mobile
+			uni.showLoading({ mask: true })
+			sendLoginSms({
+				mobile: mobile
+			}).then(res => {
+				uni.hideLoading()
+				if (res.code == 1) {
+					this.$msg('验证码已发送')
+					uni.setStorageSync('last_sms_sent_time_login_hd', Date.now())
+					this.startCountdown(60)
+				}
+			}).catch(() => {
+				uni.hideLoading()
+			})
+		},
+		/**
+		 * 【用途】短信验证码登录提交逻辑
+		 * 【为什么】校验手机号、验证码并确保用户同意服务协议后,调用后端 smsLogin 接口,登录成功后存储 Token 并初始化店铺数据
+		 */
+		smsLoginFun() {
+			let that = this
+			let mobile = this.mobile
+			let code = this.smsCode
+			if (that.$util.isEmpty(mobile)) {
+				this.$msg('请输入手机号')
+				return false
+			}
+			if (this.$util.checkMobile(mobile) == false) {
+				this.$msg('请输入正确手机号')
+				return false
+			}
+			if (that.$util.isEmpty(code)) {
+				this.$msg('请输入验证码')
+				return false
+			}
+
+			// 检查协议
+			this.checkAgreement(function() {
+				uni.showLoading({ mask: true })
+
+				smsLogin({ mobile, code, deviceId: uni.getStorageSync('deviceId') }).then(subRes => {
+					uni.hideLoading()
+
+					if (subRes.code == 1) {
+						if (that.$util.isEmpty(subRes)) {
+							return false
+						}
+
+						uni.setStorageSync('token', subRes.data.token)
+						uni.setStorageSync('shopId', subRes.data.admin.currentShopId)
+						uni.setStorageSync('reachVip', subRes.data.reachVip)
+						that.$store.commit('setLoginInfo', { ...subRes.data.admin, ...subRes.data })
+						uni.removeStorageSync('manualLogout')
+
+						getDictionaries({ token: subRes.data.token }).then(res => {
+							if (that.$util.isEmpty(res)) {
+								return false
+							}
+							that.$store.commit('setDictionariesInfo', { ...res.data.dict, ...res.data })
+							that.$store.commit('setMyShopInfo', { ...res.data.shop, ...res.data })
+						})
+
+						getShopInfo(true).then(res => { uni.setNavigationBarTitle({ title: res.name }) })
+						that.setUserShopAll()
+						// #ifdef APP-PLUS
+						that.$util.afterAppLoginSuccess({ auto: 0 })
+						// #endif
+
+						//返回上一页带参数
+						let pages = getCurrentPages();
+						let prevPage = pages[ pages.length - 2 ];
+						if (prevPage && prevPage.$vm) {
+							prevPage.$vm.backAction = 'login'
+							uni.navigateBack()
+						} else {
+							// H5 直接访问登录页,没有上一页,回首页
+							uni.reLaunch({ url: '/admin/home/workbench' })
+						}
+					}
+				})
+			})
 		}
 	}
 };
@@ -673,7 +889,7 @@ export default {
   display: flex;
   flex-direction: row;
   align-items: center;
-  justify-content: space-between;
+  justify-content: center;
   margin-top: 36upx;
   padding-top: 28upx;
   border-top: 1upx solid #f0f2f1;
@@ -685,6 +901,12 @@ export default {
   line-height: 42upx;
 }
 
+.login-footer-divider {
+  margin: 0 20upx;
+  color: #d8dee8;
+  font-size: 24upx;
+}
+
 .login-footer-link--register {
   color: #09c567;
   font-weight: 600;

+ 6 - 0
hdApp/src/api/common/index.js

@@ -50,5 +50,11 @@ export const initSocket = async clientId => {
 //app登陆
 export const appLogin = data => https.post("/auth/app-login", data);
 
+// 发送登录验证码
+export const sendLoginSms = data => https.post("/auth/send-login-sms", data);
+
+// 短信验证码登录
+export const smsLogin = data => https.post("/auth/sms-login", data);
+
 //app重新获取登陆信息
 export const hasTokenAutoLogin = data => https.get("/admin/has-token-auto-login", data);