| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480 |
- <template>
- <!-- 登录页面 -->
- <div class="login-content">
- <!-- 背景图容器 -->
- <div
- class="bg-image-container"
- :style="{ backgroundImage: `url(${BGImages})`, backgroundSize: 'cover' }"
- ></div>
- <!-- 登录输入框 -->
- <div class="login-wrapper">
- <div class="login-input">
- <span>{{ appTitle }}</span>
- <el-form
- ref="loginFormRef"
- :model="loginData.loginForm"
- :rules="rules"
- label-width="0px"
- class="input-item"
- >
- <el-form-item prop="tenantName">
- <el-input v-show="!tenantNameQuery"
- v-model="loginData.loginForm.tenantName"
- :prefix-icon="HomeFilled"
- placeholder="学校"
- />
- </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="handleGetSmsCode"
- :disabled="countingDown"
- class="get-code-btn"
- :loading="sendingCode"
- >
- {{ countingDown ? `${countDown}秒后重新获取` : '获取验证码' }}
- </el-button>
- </div>
- </el-form-item>
- </template>
- <!-- 登录按钮 -->
- <el-form-item>
- <el-button class="login-btn" type="primary" @click="handleLogin">登录</el-button>
- </el-form-item>
- </el-form>
- <!-- 多选框 -->
- <div class="check-box">
- <router-link :to="{path: '/register-login', query: {bgImage: 'blocklyBG', loginType: 'blocklyLogin'}}" class="register-link">没有账号?立即注册</router-link>
- <el-checkbox
- v-model="loginData.loginForm.rememberMe"
- label="记住我"
- size="large"
- />
- </div>
- </div>
- </div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, onUnmounted } from 'vue'
- import { useRouter } from 'vue-router'
- import { HomeFilled, Avatar, Lock, Iphone } from '@element-plus/icons-vue'
- import { ElMessage } from 'element-plus'
- import BGImages from '@/assets/images/blocklyBG.png'
- import {
- createLoginData,
- createVerificationCodeLogic,
- getTenantId,
- loginLogic,
- loadLoginData,
- checkLoginStatus,
- generateRules
- } from '@/utils/loginUtils.js'
- import {blocklyRoutes} from "@/router/index.js";
- const router = useRouter()
- // 获取环境变量
- const appTitle = import.meta.env.VITE_APP_TITLE
- const loginFormRef = ref(null)
- // 初始化登录数据
- const loginData = createLoginData()
- // 初始化验证码逻辑
- const { countingDown, countDown, sendingCode, getSmsCode, clearCountDownTimer } = createVerificationCodeLogic()
- // 登录状态标识
- const isLoggedIn = ref(false)
- // 地址栏传参默认值
- const tenantNameQuery = ref()
- // 授权状态 默认授权
- const isAuthorized = ref(true)
- // 生成表单验证规则
- const rules = generateRules(isAuthorized)
- // 获取短信验证码
- const handleGetSmsCode = async () => {
- // 先验证租户和手机号是否填写
- if (!loginData.value.loginForm.tenantName) {
- ElMessage.warning('请先输入学校名称')
- return
- }
- if (!loginData.value.loginForm.phoneNumber) {
- ElMessage.warning('请先输入手机号')
- return
- }
- // 验证租户是否存在
- const tenantId = await getTenantId(loginData.value.loginForm.tenantName)
- if (!tenantId) {
- return
- }
- // 调用验证码逻辑
- getSmsCode(tenantId, loginData.value.loginForm.tenantName, loginData.value.loginForm.phoneNumber)
- }
- // 登录
- const handleLogin = async () => {
- if (!loginFormRef.value) return
- await loginFormRef.value.validate(async valid => {
- if (valid) {
- // 先验证租户是否存在
- const tenantId = await getTenantId(loginData.value.loginForm.tenantName)
- if (!tenantId) {
- // 租户验证失败,不执行登录
- return
- }
- // 调用登录逻辑
- await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, blocklyRoutes.home)
- }
- })
- }
- // 在组件挂载时检查登录状态和恢复登录信息
- onMounted(() => {
- // 加载本地存储的登录数据
- loadLoginData(loginData)
- // 检查地址栏是否有tenantName参数
- let tenantName = router.currentRoute.value.meta?.TENANT;
- if (tenantName && tenantName === "内部测试租户") {
- loginData.value.loginForm.tenantName = tenantNameQuery.value = tenantName
- } else if (Object.keys(router.currentRoute.value.query).length > 0) {
- // 其他参数,重定向到登录页
- router.replace(blocklyRoutes.login)
- }
- // 检查登录状态,如果已登录则直接跳转到首页
- checkLoginStatus(router, blocklyRoutes.home)
- const handleKeyPress = (event) => {
- // 检查是否按下回车键(keyCode 13)
- if (event.key === 'Enter' || event.keyCode === 13) {
- handleLogin()
- }
- }
- document.addEventListener('keydown', handleKeyPress)
- // 在组件卸载时移除事件监听
- onUnmounted(() => {
- document.removeEventListener('keydown', handleKeyPress)
- clearCountDownTimer()
- })
- })
- </script>
- <style scoped lang="scss">
- @use 'sass:math';
- // 定义rpx转换函数
- @function rpx($px) {
- @return math.div($px, 750) * 100vw;
- }
- .login-content {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- display: flex;
- flex-direction: row; // 修改为水平布局
- }
- .bg-image-container {
- flex: 3; // 背景图占比为 3
- background-size: cover;
- background-position: center;
- }
- .login-wrapper {
- flex: 1; // 登录框占比为 1
- background: linear-gradient(to bottom, #001169, #8a78d0);
- padding: 20px;
- position: static;
- transform: none;
- display: flex; // 添加 Flexbox 布局
- justify-content: center; // 水平居中
- align-items: center; // 垂直居中
- }
- .login-input {
- width: rpx(190);
- height: rpx(240);
- display: flex;
- justify-content: center; // 水平居中
- align-items: center; // 垂直居中
- flex-direction: column; // 子元素垂直排列
- text-align: center; // 文本居中
- }
- .login-input span{
- color: white;
- font-size: rpx(11);
- padding-bottom: rpx(5);
- letter-spacing: rpx(1);
- }
- .input-item {
- display: flex;
- flex-direction: column; // 子元素垂直排列
- justify-content: center; // 内容垂直居中
- align-items: center; // 内容水平居中
- }
- .el-input ::v-deep(.el-input__wrapper){
- border-radius: rpx(5);
- }
- .input-item .el-form-item {
- margin-bottom: 0;
- }
- .input-item .el-input {
- width: rpx(150);
- height: rpx(22);
- margin-bottom: rpx(15);
- font-size: rpx(7);
- }
- .el-form-item ::v-deep(.el-form-item__error) {
- top: rpx(25);
- }
- .login-btn {
- width: rpx(150);
- height: rpx(22);
- color: black;
- font-size: rpx(8);
- letter-spacing: rpx(10);
- border-radius: rpx(5);
- 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);
- margin: rpx(5) auto;
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .check-box .el-checkbox {
- color: white;
- // padding-right: rpx(10);
- font-size: rpx(6);
- }
- .el-checkbox ::v-deep(.el-checkbox__label){
- font-size: rpx(6);
- }
- .check-box .register-link {
- color: white;
- font-size: rpx(6);
- text-decoration: none;
- margin-right: rpx(5);
- }
- .check-box .register-link:hover {
- text-decoration: underline;
- }
- .check-box .forgot-password {
- color: white;
- font-size: rpx(6);
- text-decoration: none;
- }
- // 短信验证码容器样式
- .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;
- }
- // 移动端响应式设计
- @media screen and (max-width: 768px) {
- .login-content {
- flex-direction: column;
- }
-
- .bg-image-container {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: -1;
- background-size: cover;
- background-position: center;
- }
-
- .login-wrapper {
- flex: none;
- width: 70%;
- margin: 0 auto;
- padding: 30px;
- background: rgba(0, 17, 105, 0.85);
- border-radius: 15px;
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- min-height: 40%;
- overflow-y: auto;
- }
-
- .login-input {
- width: 100%;
- height: 100%;
- display: flex;
- flex-direction: column;
- justify-content: center;
- }
-
- .login-input span {
- font-size: rpx(18);
- padding-bottom: rpx(20);
- }
-
- .input-item {
- width: 100%;
- align-items: stretch;
- }
-
- .input-item .el-input {
- width: 100% !important;
- max-width: none;
- height: rpx(45) !important;
- font-size: rpx(14) !important;
- margin-bottom: rpx(25) !important;
- }
-
- .el-input ::v-deep(.el-input__wrapper) {
- height: rpx(45) !important;
- }
-
- .el-input ::v-deep(.el-input__inner) {
- height: rpx(45) !important;
- font-size: rpx(14) !important;
- }
-
- .login-btn {
- width: 100% !important;
- max-width: none;
- height: rpx(45) !important;
- font-size: rpx(16) !important;
- margin: rpx(25) 0 !important;
- letter-spacing: rpx(15);
- }
-
- .check-box {
- width: 100% !important;
- max-width: none;
- height: auto;
- margin: rpx(15) 0 !important;
- }
-
- .check-box .el-checkbox {
- font-size: rpx(12) !important;
- }
-
- .check-box .register-link {
- font-size: rpx(12) !important;
- margin-right: rpx(15) !important;
- }
-
- .el-checkbox ::v-deep(.el-checkbox__label) {
- font-size: rpx(12) !important;
- }
-
- .sms-code-container {
- width: 100% !important;
- max-width: none;
- height: rpx(45) !important;
- }
-
- .sms-input {
- width: calc(100% - rpx(75)) !important;
- height: rpx(45) !important;
- }
-
- .get-code-btn {
- width: rpx(75) !important;
- height: rpx(45) !important;
- font-size: rpx(10) !important;
- }
-
- .el-form-item ::v-deep(.el-form-item__error) {
- top: rpx(50) !important;
- font-size: rpx(10) !important;
- }
- }
- </style>
|