login.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <script setup lang="ts">
  2. import { encrypt, decrypt } from "@@/utils/crypto"
  3. import { VbUtil } from "@@/vb-dom"
  4. import appStore from "@s"
  5. import apis from "@a"
  6. import type { LoginData, TenantVO } from "@@/types/Account"
  7. const userStore = appStore.authStore
  8. const router = useRouter()
  9. const loginFormRef = ref<HTMLFormElement>()
  10. const show = ref(false)
  11. const copyright = computed(() => {
  12. return appStore.appConfigStore.getConfig().copyrightYear || "2020"
  13. })
  14. const title = ref(import.meta.env.VITE_APP_TITLE || "后台管理系统")
  15. const tenantOptions = ref<TenantVO[]>([])
  16. const loginForm = ref<LoginData>({
  17. tenantId: "000000",
  18. username: "admin",
  19. password: "123iwb",
  20. rememberMe: false,
  21. code: "",
  22. uuid: ""
  23. })
  24. const loginRules = {
  25. tenantId: [{ required: true, trigger: "blur", message: "请输入您的租户编号" }],
  26. username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
  27. password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
  28. code: [{ required: true, trigger: "change", message: "请输入验证码" }]
  29. }
  30. const codeUrl = ref("")
  31. const loading = ref(false)
  32. // 租户开关
  33. const tenantEnabled = ref(false)
  34. // 验证码开关
  35. const captchaEnabled = ref(false)
  36. // 注册开关
  37. const register = ref(false)
  38. const redirect = ref(undefined)
  39. function handleLogin() {
  40. loginFormRef.value?.validate((valid: boolean) => {
  41. if (valid) {
  42. loading.value = true
  43. //勾选了需要记住密码设置在 cookie 中设置记住用户名和密码
  44. if (loginForm.value.rememberMe) {
  45. VbUtil.CookieUtil.set("tenantId", loginForm.value.tenantId, { expires: 30 })
  46. VbUtil.CookieUtil.set("username", loginForm.value.username || "", { expires: 30 })
  47. VbUtil.CookieUtil.set("password", encrypt(loginForm.value.password || ""), {
  48. expires: 30
  49. })
  50. VbUtil.CookieUtil.set("rememberMe", loginForm.value.rememberMe ? "1" : "0", {
  51. expires: 30
  52. })
  53. } else {
  54. // 否则移除
  55. VbUtil.CookieUtil.delete("tenantId")
  56. VbUtil.CookieUtil.delete("username")
  57. VbUtil.CookieUtil.delete("password")
  58. VbUtil.CookieUtil.delete("rememberMe")
  59. }
  60. userStore
  61. .login(loginForm.value)
  62. .then(() => {
  63. router.push({ path: redirect.value || "/" })
  64. loading.value = false
  65. })
  66. .catch(() => {
  67. loading.value = false
  68. // 重新获取验证码
  69. if (captchaEnabled.value) {
  70. getCode()
  71. }
  72. })
  73. }
  74. })
  75. }
  76. const doSocialLogin = (type: string) => {
  77. if (loginForm.value.tenantId === "") {
  78. message.msgError("请选择租户")
  79. return
  80. }
  81. appStore.tenantStore.setTenantId(loginForm.value.tenantId)
  82. appStore.authStore.socialLogin(type).then((res: any) => {
  83. if (res.code === HttpStatus.SUCCESS) {
  84. // 获取授权地址跳转
  85. window.location.href = res.data
  86. } else {
  87. message.msgError(res.msg)
  88. }
  89. })
  90. }
  91. function getCode() {
  92. apis.loginApi.getCodeImg().then(({ data }) => {
  93. if (captchaEnabled.value) {
  94. codeUrl.value = "data:image/gif;base64," + data.img
  95. loginForm.value.uuid = data.uuid
  96. }
  97. })
  98. }
  99. function getCookie() {
  100. try {
  101. const tenantId = VbUtil.CookieUtil.get("tenantId")
  102. const username = VbUtil.CookieUtil.get("username")
  103. const password = VbUtil.CookieUtil.get("password")
  104. const rememberMe = VbUtil.CookieUtil.get("rememberMe")
  105. loginForm.value = {
  106. tenantId: tenantId === undefined ? loginForm.value.tenantId : tenantId,
  107. username: username === undefined ? loginForm.value.username : username,
  108. password: password === undefined ? loginForm.value.password : decrypt(password) + "",
  109. rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
  110. code: "",
  111. uuid: ""
  112. }
  113. } catch (e) {
  114. console.log("GET_COOKIE", e)
  115. }
  116. }
  117. const initTenant = async () => {
  118. appStore.tenantStore
  119. .initTenant()
  120. .then((res: any) => {
  121. tenantOptions.value = res
  122. })
  123. .catch(() => {
  124. tenantOptions.value = []
  125. })
  126. }
  127. function init() {
  128. appStore.appConfigStore
  129. .loadConfig()
  130. .then(() => {
  131. console.log("---", appStore.appConfigStore.getConfig())
  132. title.value = appStore.appConfigStore.getConfig().name
  133. register.value = appStore.appConfigStore.isRegister()
  134. if (appStore.appConfigStore.isTenant()) {
  135. tenantEnabled.value = true
  136. initTenant()
  137. }
  138. if (appStore.appConfigStore.isCaptcha()) {
  139. captchaEnabled.value = true
  140. getCode()
  141. }
  142. })
  143. .finally(() => {
  144. show.value = true
  145. })
  146. getCookie()
  147. }
  148. onMounted(init)
  149. watch(
  150. () => router.currentRoute.value,
  151. (newRoute: any) => {
  152. redirect.value = newRoute.query && newRoute.query.redirect
  153. },
  154. { immediate: true }
  155. )
  156. </script>
  157. <template>
  158. <div class="login">
  159. <el-form
  160. v-if="show"
  161. ref="loginFormRef"
  162. :model="loginForm"
  163. :rules="loginRules"
  164. class="login-form">
  165. <h3 class="title">{{ title }}</h3>
  166. <el-form-item prop="tenantId" v-if="tenantEnabled">
  167. <el-select
  168. v-model="loginForm.tenantId"
  169. size="large"
  170. filterable
  171. placeholder="请选择/输入公司名称"
  172. style="width: 100%">
  173. <el-option
  174. v-for="item in tenantOptions"
  175. :key="item.tenantId"
  176. :label="item.companyName"
  177. :value="item.tenantId"></el-option>
  178. <template #prefix>
  179. <span class="el-input__icon input-icon"><VbIcon icon-name="home" class="fs-3" /></span>
  180. </template>
  181. </el-select>
  182. </el-form-item>
  183. <el-form-item prop="username">
  184. <el-input
  185. v-model="loginForm.username"
  186. type="text"
  187. size="large"
  188. auto-complete="off"
  189. placeholder="账号">
  190. <template #prefix>
  191. <span class="el-input__icon input-icon"><VbIcon icon-name="user" class="fs-3" /></span>
  192. </template>
  193. </el-input>
  194. </el-form-item>
  195. <el-form-item prop="password">
  196. <el-input
  197. v-model="loginForm.password"
  198. type="password"
  199. size="large"
  200. auto-complete="off"
  201. placeholder="密码"
  202. @keyup.enter="handleLogin">
  203. <template #prefix>
  204. <span class="el-input__icon input-icon"><VbIcon icon-name="lock" class="fs-3" /></span>
  205. </template>
  206. </el-input>
  207. </el-form-item>
  208. <el-form-item prop="code" v-if="captchaEnabled">
  209. <el-input
  210. v-model="loginForm.code"
  211. size="large"
  212. auto-complete="off"
  213. placeholder="验证码"
  214. style="width: 63%"
  215. @keyup.enter="handleLogin">
  216. <template #prefix>
  217. <span class="el-input__icon input-icon">
  218. <VbIcon icon-name="shield" class="fs-3" />
  219. </span>
  220. </template>
  221. </el-input>
  222. <div class="login-code">
  223. <img :src="codeUrl" @click="getCode" class="login-code-img" />
  224. </div>
  225. </el-form-item>
  226. <el-checkbox v-model="loginForm.rememberMe" style="margin: 0px 0px 25px 0px">
  227. 记住密码
  228. </el-checkbox>
  229. <!-- <el-form-item style="float: right">
  230. <el-button circle title="微信登录" @click="doSocialLogin('wechat')">
  231. <svg-icon icon-class="wechat" />
  232. </el-button>
  233. <el-button circle title="MaxKey登录" @click="doSocialLogin('maxkey')">
  234. <svg-icon icon-class="maxkey" />
  235. </el-button>
  236. <el-button circle title="Gitee登录" @click="doSocialLogin('gitee')">
  237. <svg-icon icon-class="gitee" />
  238. </el-button>
  239. <el-button circle title="Github登录" @click="doSocialLogin('github')">
  240. <svg-icon icon-class="github" />
  241. </el-button>
  242. </el-form-item> -->
  243. <el-form-item style="width: 100%">
  244. <el-button
  245. :loading="loading"
  246. size="large"
  247. type="primary"
  248. style="width: 100%"
  249. @click.prevent="handleLogin">
  250. <span v-if="!loading">登 录</span>
  251. <span v-else>登 录 中...</span>
  252. </el-button>
  253. <div style="float: right" v-if="register">
  254. <router-link class="link-type" :to="'/register'">立即注册</router-link>
  255. </div>
  256. </el-form-item>
  257. </el-form>
  258. <!-- 底部 -->
  259. <div class="el-login-footer">
  260. <span>
  261. Copyright ©{{ copyright }}-{{ new Date().getFullYear() }} VAP All Rights Reserved.
  262. </span>
  263. </div>
  264. </div>
  265. </template>
  266. <style lang="scss" scoped>
  267. .login {
  268. display: flex;
  269. justify-content: center;
  270. align-items: center;
  271. height: 100%;
  272. background-image: url("/media/auth/bg5.jpg");
  273. background-size: cover;
  274. }
  275. .title {
  276. margin: 0px auto 30px auto;
  277. text-align: center;
  278. color: #707070;
  279. }
  280. .login-form {
  281. border-radius: 6px;
  282. background: rgba(255, 255, 255, 0.9);
  283. width: 400px;
  284. padding: 25px 25px 5px 25px;
  285. .el-input {
  286. height: 40px;
  287. input {
  288. height: 40px;
  289. }
  290. }
  291. .input-icon {
  292. height: 39px;
  293. width: 14px;
  294. margin-left: 0px;
  295. }
  296. }
  297. .login-tip {
  298. font-size: 13px;
  299. text-align: center;
  300. color: #bfbfbf;
  301. }
  302. .login-code {
  303. width: 33%;
  304. height: 40px;
  305. float: right;
  306. img {
  307. cursor: pointer;
  308. vertical-align: middle;
  309. }
  310. }
  311. .el-login-footer {
  312. height: 40px;
  313. line-height: 40px;
  314. position: fixed;
  315. bottom: 0;
  316. width: 100%;
  317. text-align: center;
  318. color: #fff;
  319. font-family: Arial;
  320. font-size: 12px;
  321. letter-spacing: 1px;
  322. }
  323. .login-code-img {
  324. height: 40px;
  325. padding-left: 12px;
  326. }
  327. </style>