潘志宝
2024-10-28 e370f07f4b785f50e3820eecb0a440520562fe94
提交 | 用户 | 时间
820397 1 <template>
H 2   <el-form
3     v-show="getShow"
4     ref="formLogin"
5     :model="loginData.loginForm"
6     :rules="LoginRules"
7     class="login-form"
8     label-position="top"
9     label-width="120px"
10     size="large"
11   >
12     <el-row style="margin-right: -10px; margin-left: -10px">
13       <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
14         <el-form-item>
15           <LoginFormTitle style="width: 100%" />
16         </el-form-item>
17       </el-col>
18       <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
19         <el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
20           <el-input
21             v-model="loginData.loginForm.tenantName"
22             :placeholder="t('login.tenantNamePlaceholder')"
23             :prefix-icon="iconHouse"
24             link
25             type="primary"
26           />
27         </el-form-item>
28       </el-col>
29       <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
30         <el-form-item prop="username">
31           <el-input
32             v-model="loginData.loginForm.username"
33             :placeholder="t('login.usernamePlaceholder')"
34             :prefix-icon="iconAvatar"
35           />
36         </el-form-item>
37       </el-col>
38       <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
39         <el-form-item prop="password">
40           <el-input
41             v-model="loginData.loginForm.password"
42             :placeholder="t('login.passwordPlaceholder')"
43             :prefix-icon="iconLock"
44             show-password
45             type="password"
46             @keyup.enter="getCode()"
47           />
48         </el-form-item>
49       </el-col>
50       <el-col
51         :span="24"
52         style="padding-right: 10px; padding-left: 10px; margin-top: -20px; margin-bottom: -20px"
53       >
54         <el-form-item>
55           <el-row justify="space-between" style="width: 100%">
56             <el-col :span="6">
57               <el-checkbox v-model="loginData.loginForm.rememberMe">
58                 {{ t('login.remember') }}
59               </el-checkbox>
60             </el-col>
61             <el-col :offset="6" :span="12">
62               <el-link style="float: right" type="primary">{{ t('login.forgetPassword') }}</el-link>
63             </el-col>
64           </el-row>
65         </el-form-item>
66       </el-col>
67       <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
68         <el-form-item>
69           <XButton
70             :loading="loginLoading"
71             :title="t('login.login')"
72             class="w-[100%]"
73             type="primary"
74             @click="getCode()"
75           />
76         </el-form-item>
77       </el-col>
78       <Verify
79         ref="verify"
80         :captchaType="captchaType"
81         :imgSize="{ width: '400px', height: '200px' }"
82         mode="pop"
83         @success="handleLogin"
84       />
85     </el-row>
86   </el-form>
87 </template>
88 <script lang="ts" setup>
89 import { ElLoading } from 'element-plus'
90 import LoginFormTitle from './LoginFormTitle.vue'
91 import type { RouteLocationNormalizedLoaded } from 'vue-router'
92
93 import { useIcon } from '@/hooks/web/useIcon'
94
95 import * as authUtil from '@/utils/auth'
96 import { usePermissionStore } from '@/store/modules/permission'
97 import * as LoginApi from '@/api/login'
98 import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
99
100 defineOptions({ name: 'LoginForm' })
101
102 const { t } = useI18n()
103 const iconHouse = useIcon({ icon: 'ep:house' })
104 const iconAvatar = useIcon({ icon: 'ep:avatar' })
105 const iconLock = useIcon({ icon: 'ep:lock' })
106 const formLogin = ref()
107 const { validForm } = useFormValid(formLogin)
effbd8 108 const { getLoginState } = useLoginState()
820397 109 const { currentRoute, push } = useRouter()
H 110 const permissionStore = usePermissionStore()
111 const redirect = ref<string>('')
112 const loginLoading = ref(false)
113 const verify = ref()
114 const captchaType = ref('blockPuzzle') // blockPuzzle 滑块 clickWord 点击文字
115
116 const getShow = computed(() => unref(getLoginState) === LoginStateEnum.LOGIN)
117
118 const LoginRules = {
119   tenantName: [required],
120   username: [required],
121   password: [required]
122 }
123 const loginData = reactive({
124   isShowPassword: false,
125   captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE,
126   tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
127   loginForm: {
128     tenantName: import.meta.env.VITE_APP_DEFAULT_LOGIN_TENANT || '',
129     username: import.meta.env.VITE_APP_DEFAULT_LOGIN_USERNAME || '',
130     password: import.meta.env.VITE_APP_DEFAULT_LOGIN_PASSWORD || '',
131     captchaVerification: '',
132     rememberMe: true // 默认记录我。如果不需要,可手动修改
133   }
134 })
135
136 // 获取验证码
137 const getCode = async () => {
138   // 情况一,未开启:则直接登录
139   if (loginData.captchaEnable === 'false') {
140     await handleLogin({})
141   } else {
142     // 情况二,已开启:则展示验证码;只有完成验证码的情况,才进行登录
143     // 弹出验证码
144     verify.value.show()
145   }
146 }
147 // 获取租户 ID
148 const getTenantId = async () => {
149   if (loginData.tenantEnable === 'true') {
150     const res = await LoginApi.getTenantIdByName(loginData.loginForm.tenantName)
151     authUtil.setTenantId(res)
152   }
153 }
154 // 记住我
155 const getLoginFormCache = () => {
156   const loginForm = authUtil.getLoginForm()
157   if (loginForm) {
158     loginData.loginForm = {
159       ...loginData.loginForm,
160       username: loginForm.username ? loginForm.username : loginData.loginForm.username,
161       password: loginForm.password ? loginForm.password : loginData.loginForm.password,
162       rememberMe: loginForm.rememberMe,
163       tenantName: loginForm.tenantName ? loginForm.tenantName : loginData.loginForm.tenantName
164     }
165   }
166 }
167 // 根据域名,获得租户信息
168 const getTenantByWebsite = async () => {
169   const website = location.host
170   const res = await LoginApi.getTenantByWebsite(website)
171   if (res) {
172     loginData.loginForm.tenantName = res.name
173     authUtil.setTenantId(res.id)
174   }
175 }
176 const loading = ref() // ElLoading.service 返回的实例
177 // 登录
178 const handleLogin = async (params) => {
179   loginLoading.value = true
180   try {
181     await getTenantId()
182     const data = await validForm()
183     if (!data) {
184       return
185     }
186     loginData.loginForm.captchaVerification = params.captchaVerification
187     const res = await LoginApi.login(loginData.loginForm)
188     if (!res) {
189       return
190     }
191     loading.value = ElLoading.service({
192       lock: true,
193       text: '正在加载系统中...',
194       background: 'rgba(0, 0, 0, 0.7)'
195     })
196     if (loginData.loginForm.rememberMe) {
197       authUtil.setLoginForm(loginData.loginForm)
198     } else {
199       authUtil.removeLoginForm()
200     }
201     authUtil.setToken(res)
202     if (!redirect.value) {
effbd8 203       redirect.value = '/index'
820397 204     }
effbd8 205     // 判断是否为SSO登录
H 206     if (redirect.value.indexOf('sso') !== -1) {
207       window.location.href = window.location.href.replace('/login?redirect=', '')
208     } else {
209       push({ path: redirect.value || permissionStore.addRouters[0].path })
210     }
820397 211   } finally {
H 212     loginLoading.value = false
213     loading.value.close()
214   }
215 }
216
217 watch(
218   () => currentRoute.value,
219   (route: RouteLocationNormalizedLoaded) => {
220     redirect.value = route?.query?.redirect as string
221   },
222   {
223     immediate: true
224   }
225 )
226 onMounted(() => {
227   getLoginFormCache()
228   getTenantByWebsite()
229 })
230 </script>
231
232 <style lang="scss" scoped>
233 :deep(.anticon) {
234   &:hover {
235     color: var(--el-color-primary) !important;
236   }
237 }
238
239 .login-code {
240   float: right;
241   width: 100%;
242   height: 38px;
243
244   img {
245     width: 100%;
246     height: auto;
247     max-width: 100px;
248     vertical-align: middle;
249     cursor: pointer;
250   }
251 }
252 </style>