LoginForm.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. <template>
  2. <el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" size="large">
  3. <el-form-item prop="username">
  4. <el-input v-model="loginForm.username" placeholder="请输入用户名">
  5. <template #prefix>
  6. <el-icon class="el-input__icon">
  7. <user />
  8. </el-icon>
  9. </template>
  10. </el-input>
  11. </el-form-item>
  12. <el-form-item prop="password">
  13. <el-input v-model="loginForm.password" type="password" placeholder="请输入密码" show-password autocomplete="new-password">
  14. <template #prefix>
  15. <el-icon class="el-input__icon">
  16. <lock />
  17. </el-icon>
  18. </template>
  19. </el-input>
  20. </el-form-item>
  21. <el-form-item prop="captcha">
  22. <div class="captcha-row">
  23. <el-input v-model="loginForm.captcha" placeholder="请输入验证码" maxlength="6" />
  24. <div class="captcha-code" @click="refreshCaptcha" title="点击刷新验证码">
  25. {{ captchaCode }}
  26. </div>
  27. </div>
  28. </el-form-item>
  29. </el-form>
  30. <div class="login-btn">
  31. <el-button :icon="CircleClose" round size="large" @click="resetForm(loginFormRef)"> 重置 </el-button>
  32. <el-button :icon="UserFilled" round size="large" type="primary" :loading="loading" @click="login(loginFormRef)">
  33. 登录
  34. </el-button>
  35. </div>
  36. <!-- <div class="form-footer">
  37. 还没有账号?
  38. <el-button link type="primary" @click="goRegister">立即注册</el-button>
  39. </div> -->
  40. </template>
  41. <script setup lang="ts">
  42. import { ref, reactive, onMounted, onBeforeUnmount } from "vue";
  43. import { useRouter } from "vue-router";
  44. import { HOME_URL, REGISTER_URL } from "@/config";
  45. // import { getTimeState } from "@/utils";
  46. import { Login } from "@/api/interface/index";
  47. import { ElNotification } from "element-plus";
  48. import { loginApi } from "@/api/modules/login";
  49. import { useUserStore } from "@/stores/modules/user";
  50. import { useTabsStore } from "@/stores/modules/tabs";
  51. import { useKeepAliveStore } from "@/stores/modules/keepAlive";
  52. import { initDynamicRouter } from "@/routers/modules/dynamicRouter";
  53. import { CircleClose, UserFilled } from "@element-plus/icons-vue";
  54. import type { ElForm } from "element-plus";
  55. import md5 from "md5";
  56. const router = useRouter();
  57. const userStore = useUserStore();
  58. const tabsStore = useTabsStore();
  59. const keepAliveStore = useKeepAliveStore();
  60. type FormInstance = InstanceType<typeof ElForm>;
  61. const loginFormRef = ref<FormInstance>();
  62. const loginRules = reactive({
  63. username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
  64. password: [{ required: true, message: "请输入密码", trigger: "blur" }],
  65. captcha: [{ required: true, message: "请输入验证码", trigger: "blur" }]
  66. });
  67. const loading = ref(false);
  68. const loginForm = reactive<Login.ReqLoginForm & { captcha: string }>({
  69. username: "",
  70. password: "",
  71. captcha: ""
  72. });
  73. const captchaCode = ref("");
  74. const generateCaptcha = () => {
  75. captchaCode.value = Math.floor(Math.random() * 1000000)
  76. .toString()
  77. .padStart(6, "0");
  78. };
  79. const refreshCaptcha = () => {
  80. generateCaptcha();
  81. loginForm.captcha = "";
  82. };
  83. // login
  84. const login = (formEl: FormInstance | undefined) => {
  85. if (!formEl) return;
  86. formEl.validate(async valid => {
  87. if (!valid) return;
  88. if (loginForm.captcha !== captchaCode.value) {
  89. ElNotification({
  90. title: "验证码错误",
  91. message: "请重新输入验证码",
  92. type: "error",
  93. duration: 5000
  94. });
  95. refreshCaptcha();
  96. return;
  97. }
  98. loading.value = true;
  99. try {
  100. // 1.执行登录接口
  101. const loginPayload = {
  102. username: loginForm.username,
  103. password: md5(loginForm.password)
  104. };
  105. const { data } = (await loginApi(loginPayload)) as { data: Login.ResLogin };
  106. console.log(data);
  107. if (data.result) {
  108. userStore.setToken(data.token);
  109. // 2.添加动态路由
  110. await initDynamicRouter();
  111. // 3.清空 tabs、keepAlive 数据
  112. tabsStore.setTabs([]);
  113. keepAliveStore.setKeepAliveName([]);
  114. // 4.跳转到首页
  115. router.push(HOME_URL);
  116. // ElNotification({
  117. // title: getTimeState(),
  118. // message: "欢迎登录 Geeker-Admin",
  119. // type: "success",
  120. // duration: 3000
  121. // });
  122. ElNotification({
  123. title: "登录成功",
  124. dangerouslyUseHTMLString: true,
  125. type: "success",
  126. duration: 8000
  127. });
  128. } else {
  129. ElNotification({
  130. title: "登录失败,请检查用户名和密码",
  131. dangerouslyUseHTMLString: true,
  132. type: "error",
  133. duration: 8000
  134. });
  135. }
  136. } finally {
  137. loading.value = false;
  138. }
  139. });
  140. };
  141. // resetForm
  142. const resetForm = (formEl: FormInstance | undefined) => {
  143. if (!formEl) return;
  144. formEl.resetFields();
  145. refreshCaptcha();
  146. };
  147. onMounted(() => {
  148. generateCaptcha();
  149. // 监听 enter 事件(调用登录)
  150. document.onkeydown = (e: KeyboardEvent) => {
  151. if (e.code === "Enter" || e.code === "enter" || e.code === "NumpadEnter") {
  152. if (loading.value) return;
  153. login(loginFormRef.value);
  154. }
  155. };
  156. });
  157. onBeforeUnmount(() => {
  158. document.onkeydown = null;
  159. });
  160. const goRegister = () => {
  161. router.push(REGISTER_URL);
  162. };
  163. </script>
  164. <style scoped lang="scss">
  165. @import "../index";
  166. .captcha-row {
  167. display: flex;
  168. gap: 12px;
  169. align-items: center;
  170. }
  171. .captcha-code {
  172. padding: 0 16px;
  173. font-weight: 600;
  174. color: var(--el-color-primary);
  175. text-align: center;
  176. letter-spacing: 4px;
  177. cursor: pointer;
  178. user-select: none;
  179. background-color: var(--el-color-primary-light-9);
  180. border: 1px solid var(--el-color-primary);
  181. border-radius: 6px;
  182. }
  183. </style>