|
|
@@ -24,27 +24,60 @@
|
|
|
placeholder="学校"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
- <el-form-item prop="username">
|
|
|
- <el-input
|
|
|
- v-model="loginData.loginForm.username"
|
|
|
- :prefix-icon="Avatar"
|
|
|
- placeholder="账号"
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item prop="password">
|
|
|
- <el-input
|
|
|
- v-model="loginData.loginForm.password"
|
|
|
- type="password"
|
|
|
- :prefix-icon="Lock"
|
|
|
- placeholder="密码"
|
|
|
- show-password
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
|
|
|
+ <!-- 条件显示手机号和短信验证码或账号和密码 -->
|
|
|
+ <template v-if="isAuthorized">
|
|
|
+ <el-form-item prop="username">
|
|
|
+ <el-input
|
|
|
+ v-model="loginData.loginForm.username"
|
|
|
+ :prefix-icon="Avatar"
|
|
|
+ placeholder="账号"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item prop="password">
|
|
|
+ <el-input
|
|
|
+ v-model="loginData.loginForm.password"
|
|
|
+ class="password-input"
|
|
|
+ type="password"
|
|
|
+ :prefix-icon="Lock"
|
|
|
+ placeholder="密码"
|
|
|
+ show-password
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <template v-else>
|
|
|
+ <el-form-item prop="phoneNumber">
|
|
|
+ <el-input
|
|
|
+ v-model="loginData.loginForm.phoneNumber"
|
|
|
+ :prefix-icon="Iphone"
|
|
|
+ placeholder="手机号"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ <!-- 短信验证码输入框和获取验证码按钮 -->
|
|
|
+ <el-form-item prop="smsCode">
|
|
|
+ <div class="sms-code-container">
|
|
|
+ <el-input
|
|
|
+ v-model="loginData.loginForm.smsCode"
|
|
|
+ placeholder="短信验证码"
|
|
|
+ class="sms-input"
|
|
|
+ />
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ @click="getSmsCode"
|
|
|
+ :disabled="countingDown"
|
|
|
+ class="get-code-btn"
|
|
|
+ :loading="sendingCode"
|
|
|
+ >
|
|
|
+ {{ countingDown ? `${countDown}秒后重新获取` : '获取验证码' }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </template>
|
|
|
|
|
|
<!-- 登录按钮 -->
|
|
|
<el-form-item>
|
|
|
- <el-button type="primary" @click="handleLogin">登录</el-button>
|
|
|
+ <el-button class="login-btn" type="primary" @click="handleLogin">登录</el-button>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<!-- 多选框 -->
|
|
|
@@ -54,7 +87,6 @@
|
|
|
label="记住我"
|
|
|
size="large"
|
|
|
/>
|
|
|
- <!-- <a href="javascript:;" class="forgot-password">忘记密码?</a> -->
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -62,14 +94,15 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, onMounted, computed ,onUnmounted} from 'vue'
|
|
|
+import { ref, onMounted, computed, onUnmounted, nextTick } from 'vue'
|
|
|
import { useRouter } from 'vue-router'
|
|
|
-import { HomeFilled, Avatar, Lock } from '@element-plus/icons-vue'
|
|
|
-import { getTenantIdByName, login } from '@/api/login/login.js'
|
|
|
+import { HomeFilled, Avatar, Lock,Iphone } from '@element-plus/icons-vue'
|
|
|
+import { getTenantIdByName, login,smsLogin,smsCode } from '@/api/login/login.js'
|
|
|
import { ElLoading, ElMessage } from 'element-plus'
|
|
|
|
|
|
import BGImages from '@/assets/images/homeBG.png'
|
|
|
|
|
|
+
|
|
|
const router = useRouter()
|
|
|
|
|
|
const loginFormRef = ref(null)
|
|
|
@@ -79,7 +112,9 @@ const loginData = ref({
|
|
|
tenantName: import.meta.env.VITE_APP_DEFAULT_LOGIN_TENANT || '',
|
|
|
username: import.meta.env.VITE_APP_DEFAULT_LOGIN_USERNAME || '',
|
|
|
password: import.meta.env.VITE_APP_DEFAULT_LOGIN_PASSWORD || '',
|
|
|
- rememberMe: false // 记住
|
|
|
+ smsCode: '', // 短信验证码字段
|
|
|
+ rememberMe: false, // 记住
|
|
|
+ phoneNumber: '' // 手机号字段
|
|
|
}
|
|
|
})
|
|
|
|
|
|
@@ -89,17 +124,42 @@ const tenantId = ref('')
|
|
|
// 登录状态标识
|
|
|
const isLoggedIn = ref(false)
|
|
|
|
|
|
-// 输入框校验
|
|
|
-const rules = {
|
|
|
- tenantName: [{ required: true, message: '请输入学校名称', trigger: 'blur' }],
|
|
|
- username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
|
|
|
- password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
|
|
|
-}
|
|
|
+// 短信验证码相关状态
|
|
|
+const countingDown = ref(false)
|
|
|
+const countDown = ref(60)
|
|
|
+const sendingCode = ref(false)
|
|
|
+let countDownTimer = null
|
|
|
+
|
|
|
+// 授权状态 默认授权
|
|
|
+const isAuthorized = ref(true)
|
|
|
+
|
|
|
+// 输入框校验 - 根据授权状态动态调整
|
|
|
+const rules = computed(() => {
|
|
|
+ if (isAuthorized.value) {
|
|
|
+ // 授权状态:需要账号和密码
|
|
|
+ return {
|
|
|
+ tenantName: [{ required: true, message: '请输入学校名称', trigger: 'blur' }],
|
|
|
+ username: [{ required: true, message: '请输入账号', trigger: 'blur' }],
|
|
|
+ password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 未授权状态:需要手机号和短信验证码
|
|
|
+ return {
|
|
|
+ tenantName: [{ required: true, message: '请输入学校名称', trigger: 'blur' }],
|
|
|
+ phoneNumber: [
|
|
|
+ { required: true, message: '请输入手机号', trigger: 'blur' },
|
|
|
+ { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
|
|
|
+ ],
|
|
|
+ smsCode: [{ required: true, message: '请输入短信验证码', trigger: 'blur' }]
|
|
|
+ }
|
|
|
+ }
|
|
|
+})
|
|
|
|
|
|
// 获取租户 ID
|
|
|
const getTenantId = async () => {
|
|
|
try {
|
|
|
const res = await getTenantIdByName(loginData.value.loginForm.tenantName)
|
|
|
+ console.log(res);
|
|
|
if (res && res.data) {
|
|
|
//记录租户id
|
|
|
tenantId.value = res.data
|
|
|
@@ -115,6 +175,64 @@ const getTenantId = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// 获取短信验证码函数
|
|
|
+const getSmsCode = async () => {
|
|
|
+ // 先验证租户和手机号是否填写
|
|
|
+ if (!loginData.value.loginForm.tenantName) {
|
|
|
+ ElMessage.warning('请先输入学校名称')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (!loginData.value.loginForm.phoneNumber) {
|
|
|
+ ElMessage.warning('请先输入手机号')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 验证租户是否存在
|
|
|
+ const tenantValid = await getTenantId()
|
|
|
+ if (!tenantValid) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ sendingCode.value = true
|
|
|
+ try {
|
|
|
+ // 获取验证码
|
|
|
+ const res = await smsCode(
|
|
|
+ { 'Tenant-Id': tenantId.value },{
|
|
|
+ tenantName: loginData.value.loginForm.tenantName,
|
|
|
+ mobile: loginData.value.loginForm.phoneNumber,
|
|
|
+ scene: 21,
|
|
|
+ })
|
|
|
+ console.log('发送短信验证码:', res)
|
|
|
+ if (res.code === 0) {
|
|
|
+ ElMessage.success('验证码发送成功')
|
|
|
+ // 开始倒计时
|
|
|
+ startCountDown()
|
|
|
+ } else {
|
|
|
+ ElMessage.error(res.message || '验证码发送失败')
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ ElMessage.error('验证码发送失败,请重试')
|
|
|
+ console.error('发送验证码错误:', error)
|
|
|
+ } finally {
|
|
|
+ sendingCode.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+// 验证码倒计时函数
|
|
|
+const startCountDown = () => {
|
|
|
+ countingDown.value = true
|
|
|
+ countDown.value = 60
|
|
|
+
|
|
|
+ if (countDownTimer) {
|
|
|
+ clearInterval(countDownTimer)
|
|
|
+ }
|
|
|
+ countDownTimer = setInterval(() => {
|
|
|
+ countDown.value--
|
|
|
+ if (countDown.value <= 0) {
|
|
|
+ clearInterval(countDownTimer)
|
|
|
+ countingDown.value = false
|
|
|
+ }
|
|
|
+ }, 1000)
|
|
|
+}
|
|
|
|
|
|
// 登录
|
|
|
const handleLogin = async params => {
|
|
|
@@ -131,23 +249,32 @@ const handleLogin = async params => {
|
|
|
return
|
|
|
}
|
|
|
const loginDataLoginForm = { ...loginData.value.loginForm }
|
|
|
- // 构建包含 headers 的请求参数
|
|
|
- const res = await login(
|
|
|
- { 'Tenant-Id': tenantId.value },
|
|
|
- loginDataLoginForm
|
|
|
- )
|
|
|
- console.log(res);
|
|
|
+
|
|
|
+ // 根据授权状态选择不同的登录接口
|
|
|
+ let res
|
|
|
+ if (!isAuthorized.value) {
|
|
|
+ // 未授权状态,使用短信验证码登录
|
|
|
+ res = await smsLogin(
|
|
|
+ { 'Tenant-Id': tenantId.value },{
|
|
|
+ mobile: loginData.value.loginForm.phoneNumber,
|
|
|
+ code: loginData.value.loginForm.smsCode,
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ // 授权状态,使用账号密码登录
|
|
|
+ res = await login(
|
|
|
+ { 'Tenant-Id': tenantId.value },
|
|
|
+ loginDataLoginForm
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
if (!res) {
|
|
|
return
|
|
|
}
|
|
|
- //【补充】校验登录状态
|
|
|
- // 成功-失败-提示文字
|
|
|
- // 校验登录状态,返回数据中 code 为 200 表示成功
|
|
|
+ // 校验登录状态
|
|
|
if (res.code === 0) {
|
|
|
ElMessage.success('登录成功')
|
|
|
isLoggedIn.value = true
|
|
|
- // 存储登录状态(无论是否勾选记住我都保存基本登录状态)
|
|
|
+ // 存储登录状态
|
|
|
localStorage.setItem('isLoggedIn', 'true')
|
|
|
localStorage.setItem('token', res.data.accessToken)
|
|
|
|
|
|
@@ -176,11 +303,15 @@ const handleLogin = async params => {
|
|
|
})
|
|
|
// 登录成功后,跳转到指定的页面
|
|
|
router.push('/home')
|
|
|
+ } else if (res.code === 1002000009) {
|
|
|
+ // 未授权状态,切换到短信验证码登录
|
|
|
+ ElMessage.warning(res.msg || '登录IP未被授权,请使用手机号短信验证码登录!')
|
|
|
+ isAuthorized.value = false
|
|
|
} else {
|
|
|
- ElMessage.error(res.message || '登录失败,请检查账号密码')
|
|
|
+ ElMessage.error(res.msg || '登录失败,请检查账号密码!')
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- ElMessage.error('登录出错,请重试')
|
|
|
+ ElMessage.error('登录出错,请重试!')
|
|
|
console.error('登录错误:', error)
|
|
|
} finally {
|
|
|
loginLoading.value = false
|
|
|
@@ -231,6 +362,9 @@ onMounted(() => {
|
|
|
// 在组件卸载时移除事件监听
|
|
|
onUnmounted(() => {
|
|
|
document.removeEventListener('keydown', handleKeyPress)
|
|
|
+ if (countDownTimer) {
|
|
|
+ clearInterval(countDownTimer)
|
|
|
+ }
|
|
|
})
|
|
|
|
|
|
})
|
|
|
@@ -301,21 +435,24 @@ onMounted(() => {
|
|
|
font-size: rpx(7);
|
|
|
}
|
|
|
.el-form-item ::v-deep(.el-form-item__error) {
|
|
|
- top: 0;
|
|
|
- padding-top: rpx(25);
|
|
|
+ top: rpx(25);
|
|
|
+ // padding-top: rpx(25);
|
|
|
}
|
|
|
-.input-item .el-button {
|
|
|
+.login-btn {
|
|
|
width: rpx(150);
|
|
|
- height: rpx(20);
|
|
|
+ height: rpx(22);
|
|
|
color: black;
|
|
|
font-size: rpx(8);
|
|
|
letter-spacing: rpx(10);
|
|
|
border-radius: rpx(5);
|
|
|
- margin: rpx(10) 0 auto;
|
|
|
+ margin: rpx(15) 0 auto;
|
|
|
border: none;
|
|
|
background: linear-gradient(to bottom, #fee78a, #ffce1b);
|
|
|
box-shadow: 0 8px 8px rgb(0, 0, 0, 0.2);
|
|
|
}
|
|
|
+.password-input {
|
|
|
+ margin-bottom: rpx(0) !important;
|
|
|
+}
|
|
|
.check-box {
|
|
|
width: rpx(150);
|
|
|
height: rpx(18);
|
|
|
@@ -337,4 +474,41 @@ onMounted(() => {
|
|
|
font-size: rpx(6);
|
|
|
text-decoration: none;
|
|
|
}
|
|
|
-</style>
|
|
|
+
|
|
|
+// 短信验证码容器样式
|
|
|
+.sms-code-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ width: rpx(150);
|
|
|
+ height: rpx(22);
|
|
|
+}
|
|
|
+
|
|
|
+.sms-input {
|
|
|
+ flex: 1;
|
|
|
+ height: rpx(22);
|
|
|
+ margin-bottom: 0 !important;
|
|
|
+}
|
|
|
+.sms-input ::v-deep(.el-input__wrapper) {
|
|
|
+ border-top-right-radius: 0;
|
|
|
+ border-bottom-right-radius: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.get-code-btn {
|
|
|
+ width: rpx(40);
|
|
|
+ height: rpx(22);
|
|
|
+ margin: 0 !important;
|
|
|
+ padding: 0;
|
|
|
+ font-size: rpx(5);
|
|
|
+ border-radius: rpx(5);
|
|
|
+ border-top-left-radius: 0;
|
|
|
+ border-bottom-left-radius: 0;
|
|
|
+ letter-spacing: normal;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ background-color: #fff;
|
|
|
+ color: #919191;
|
|
|
+ // background: linear-gradient(to bottom, #78c0ff, #0070f3);
|
|
|
+ border: none;
|
|
|
+}
|
|
|
+</style>
|