shish 2 дней назад
Родитель
Сommit
dc16831324
3 измененных файлов с 201 добавлено и 50 удалено
  1. 107 34
      ghs/src/pages/login/index.js
  2. 67 16
      ghs/src/pages/login/index.vue
  3. 27 0
      ghs/src/service/common/index.js

+ 107 - 34
ghs/src/pages/login/index.js

@@ -1,35 +1,93 @@
-// import Captcha from '@/components/common/captcha';
-
+/**
+ * PC 登录页共用逻辑:账号密码走 /auth/login,短信登录走 /auth/send-login-sms、/auth/sms-login(对齐 ghsApp)。
+ */
 export default {
-	// components: {
-	// 	Captcha
-	// },
-
 	data() {
 		return {
 			form: {
 				mobile: null,
 				password: null,
-				// captchaId: '',
-				verifyCode: ''
+				verifyCode: '',
+				smsCode: ''
 			},
+			/** pwd=账号密码 / sms=短信验证码 */
+			loginMode: 'pwd',
+			smsCountdown: 0,
+			smsTimer: null,
 			saving: false,
 			loading: true
 		};
 	},
 	mounted() {
 		this.loading = false;
+		this.initSmsCountdown();
 		if (process.env.NODE_ENV === 'development') {
 			this.form.mobile = '15280215347';
 			this.form.password = 'a.1234';
 		}
 	},
+	beforeDestroy() {
+		if (this.smsTimer) {
+			clearInterval(this.smsTimer);
+		}
+	},
 
 	methods: {
 		captchaChange() {
 			this.form.verifyCode = '';
 		},
 
+		/** 与小程序 checkMobile 一致:11 位国内手机号 */
+		isMobile(mobile) {
+			return /^1\d{10}$/.test(String(mobile || ''));
+		},
+
+		/** 从 localStorage 恢复发码倒计时,避免刷新立刻再发 */
+		initSmsCountdown() {
+			const lastSent = localStorage.getItem('last_sms_sent_time_login');
+			if (!lastSent) {
+				return;
+			}
+			const passed = Math.floor((Date.now() - Number(lastSent)) / 1000);
+			if (passed < 60) {
+				this.startCountdown(60 - passed);
+			}
+		},
+
+		/** 启动验证码倒计时 */
+		startCountdown(seconds) {
+			this.smsCountdown = seconds;
+			if (this.smsTimer) {
+				clearInterval(this.smsTimer);
+			}
+			this.smsTimer = setInterval(() => {
+				this.smsCountdown--;
+				if (this.smsCountdown <= 0) {
+					clearInterval(this.smsTimer);
+				}
+			}, 1000);
+		},
+
+		/** 登录成功后拉用户/菜单并进首页,密码登录与短信登录共用 */
+		async afterLoginSuccess() {
+			if (this.$projectName == 'business') {
+				await this.$store.dispatch('userInfo');
+				await this.$store.dispatch('shopInfo');
+			} else {
+				await this.$store.dispatch('adminInfo');
+			}
+			const [first] = await this.$store.dispatch('permMenu');
+			this.saving = false;
+			if (!first) {
+				this.$message.error('该账号没有权限');
+				return;
+			}
+			this.$nextTick(() => {
+				this.$router.push('/');
+			});
+		},
+
+		/** 账号密码登录:/auth/login */
 		async next() {
 			const { mobile, password } = this.form;
 
@@ -44,37 +102,52 @@ export default {
 			this.saving = true;
 
 			try {
-				// 登录
 				await this.$store.dispatch('userLogin', this.form);
+				await this.afterLoginSuccess();
+			} catch (err) {
+				this.saving = false;
+			}
+		},
 
-				if (this.$projectName == 'business') {
-					// 用户信息
-					await this.$store.dispatch('userInfo');
-					// 商户信息
-					await this.$store.dispatch('shopInfo');
-				} else {
-					// 后台信息
-					await this.$store.dispatch('adminInfo');
-				}
-
-				// 权限菜单
-				const [first] = await this.$store.dispatch('permMenu');
+		/** 获取登录短信验证码:/auth/send-login-sms */
+		getSmsCode() {
+			const mobile = this.form.mobile;
+			if (!mobile) {
+				return this.$message.warning('请输入手机号');
+			}
+			if (!this.isMobile(mobile)) {
+				return this.$message.warning('请输入正确手机号');
+			}
+			if (this.smsCountdown > 0) {
+				return;
+			}
+			this.$service.common.sendLoginSms({ mobile }).then(() => {
+				this.$message.success('验证码已发送');
+				localStorage.setItem('last_sms_sent_time_login', String(Date.now()));
+				this.startCountdown(60);
+			});
+		},
 
-				this.saving = false;
+		/** 短信验证码登录:/auth/sms-login,成功后与密码登录同一套进首页流程 */
+		async smsNext() {
+			const mobile = this.form.mobile;
+			const code = this.form.smsCode;
+			if (!mobile) {
+				return this.$message.warning('请输入手机号');
+			}
+			if (!this.isMobile(mobile)) {
+				return this.$message.warning('请输入正确手机号');
+			}
+			if (!code) {
+				return this.$message.warning('请输入验证码');
+			}
 
-				if (!first) {
-					this.$message.error('该账号没有权限');
-				} else {
-					this.$nextTick(() => {
-						if (this.$projectName == 'business') {
-							this.$router.push('/');
-						} else {
-							this.$router.push('/');
-						}
-					});
-				}
+			this.saving = true;
+			try {
+				const res = await this.$service.common.smsLogin({ mobile, code });
+				this.$store.commit('SET_TOKEN', res);
+				await this.afterLoginSuccess();
 			} catch (err) {
-				// this.$refs.captcha.refresh();
 				this.saving = false;
 			}
 		}

+ 67 - 16
ghs/src/pages/login/index.vue

@@ -1,3 +1,7 @@
+<!--
+  PC 销花宝登录:账号密码 / 短信验证码二选一。
+  短信接口与 ghsApp login.vue 同源 /auth/send-login-sms、/auth/sms-login。
+-->
 <template>
   <div class="loginPage">
     <div class="login_main">
@@ -5,17 +9,17 @@
       <div class="login_right">
         <p>欢迎登录</p>
         <el-form ref="form" class="form" size="medium" :disabled="saving">
-				<el-form-item label="用户名">
+				<el-form-item :label="loginMode === 'sms' ? '手机号' : '用户名'">
 					<el-input
-						placeholder="请输入用户名"
+						:placeholder="loginMode === 'sms' ? '请输入手机号' : '请输入用户名'"
 						v-model="form.mobile"
 						maxlength="20"
-						@keyup.enter.native="next"
+						@keyup.enter.native="loginMode === 'sms' ? smsNext() : next()"
 						auto-complete="off"
 					></el-input>
 				</el-form-item>
 
-				<el-form-item label="密码">
+				<el-form-item v-if="loginMode === 'pwd'" label="密码">
 					<el-input
 						type="password"
 						placeholder="请输入密码"
@@ -25,7 +29,28 @@
 						@keyup.enter.native="next"
 					></el-input>
 				</el-form-item>
-				<el-button class="submit-btn" @click="next" :saving="saving">登录</el-button>
+
+				<el-form-item v-else label="验证码" class="captcha">
+					<el-input
+						placeholder="请输入验证码"
+						v-model="form.smsCode"
+						maxlength="6"
+						auto-complete="off"
+						@keyup.enter.native="smsNext"
+					></el-input>
+					<el-button
+						type="button"
+						class="value sms-code-btn"
+						:disabled="smsCountdown > 0"
+						@click="getSmsCode"
+					>{{ smsCountdown > 0 ? smsCountdown + 's' : '获取验证码' }}</el-button>
+				</el-form-item>
+				<div class="login-mode">
+					<span :class="{ active: loginMode === 'pwd' }" @click="loginMode = 'pwd'">账号密码</span>
+					<span class="login-mode-split">|</span>
+					<span :class="{ active: loginMode === 'sms' }" @click="loginMode = 'sms'">短信登录</span>
+				</div>
+				<el-button class="submit-btn" @click="loginMode === 'sms' ? smsNext() : next()" :saving="saving">登录</el-button>
 			</el-form>
       </div>
     </div>
@@ -78,13 +103,29 @@ export default {
         font-weight: 500;
         color: #3385FF;
       }
+      .login-mode{
+        margin-top: 28px;
+        margin-bottom: 0;
+        text-align: center;
+        font-size: 18px;
+        color: #9289A6;
+        span{
+          cursor: pointer;
+        }
+        .login-mode-split{
+          margin-left: 12px;
+          margin-right: 12px;
+          cursor: default;
+          color: #D8DEE8;
+        }
+        .active{
+          color: #3385FF;
+          font-weight: 600;
+        }
+      }
       .el-form {
-        // height:30vh;
         width: 83%;
         border-radius: 3px;
-        // position: absolute;
-        // right: 42px;
-        // top: 45px;
         z-index: 1;
 
         .el-form-item {
@@ -99,10 +140,6 @@ export default {
           height: 48px;
           border-radius: 8px;
           .el-input__inner {
-            // border: 0;
-            // box-shadow: 0 0 0px 1000px #fefcfe inset;
-            // border-radius: 0;
-            // padding: 0 5px;
             height: 100%;
             border: 1px solid #B1B3CC;
             font-size: 16px;
@@ -119,15 +156,29 @@ export default {
           }
         }
 
+        .sms-code-btn {
+          height: 48px;
+          padding: 0 14px;
+          border: none;
+          color: #3385FF;
+          background: transparent;
+        }
+
         .submit-btn {
+          display: flex;
+          align-items: center;
+          justify-content: center;
           width: 100%;
-          height: 45px;
-          margin-top: 50px;
+          height: 56px;
+          padding: 0;
+          margin-top: 16px;
+          line-height: 1;
           background: #3385FF;
           border-radius: 40px;
           font-size: 18px;
           font-weight: 400;
           letter-spacing: 10px;
+          text-indent: 10px;
           color: #FFFFFF;
         }
       }
@@ -135,4 +186,4 @@ export default {
   }
 }
 
-</style>
+</style>

+ 27 - 0
ghs/src/service/common/index.js

@@ -79,6 +79,33 @@ export class CommonService extends BaseService {
 		});
 	}
 
+	/**
+	 * 发送登录短信验证码。对应 AuthController::actionSendLoginSms,与 ghsApp sendLoginSms 同源。
+	 */
+	sendLoginSms({ mobile }) {
+		return this.request({
+			url: '/auth/send-login-sms',
+			method: 'POST',
+			data: {
+				mobile
+			}
+		});
+	}
+
+	/**
+	 * 短信验证码登录。对应 AuthController::actionSmsLogin,与 ghsApp smsLogin 同源。
+	 */
+	smsLogin({ mobile, code }) {
+		return this.request({
+			url: '/auth/sms-login',
+			method: 'POST',
+			data: {
+				mobile,
+				code
+			}
+		});
+	}
+
 	/**
 	 * 用户退出
 	 */