index.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <template>
  2. <div :class="classObj" class="login-container">
  3. <el-form ref="loginFormRef" :model="loginData" :rules="loginRules" class="login-form">
  4. <div class="title">
  5. <span>登录</span>
  6. </div>
  7. <el-form-item prop="phone">
  8. <span class="m-2"> <svg-icon icon-class="username" size="30px" /> </span>|
  9. <el-input
  10. ref="username"
  11. v-model="loginData.phone"
  12. class="flex-1"
  13. placeholder="请输入登录账号"
  14. name="username" />
  15. </el-form-item>
  16. <el-form-item prop="password">
  17. <span class="m-2"> <svg-icon icon-class="password" size="30px" /> </span>|
  18. <el-input
  19. v-model="loginData.password"
  20. class="flex-1"
  21. placeholder="请输入登录密码"
  22. :type="passwordVisible === false ? 'password' : 'input'"
  23. name="password"
  24. @keyup.enter="handleLogin" />
  25. <span class="mr-3" @click="passwordVisible = !passwordVisible">
  26. <svg-icon :icon-class="!passwordVisible ? 'eye' : 'eye-open'" class="cursor-pointer" size="18px" />
  27. </span>
  28. </el-form-item>
  29. <el-button
  30. v-if="loginData.phone && loginData.password"
  31. :loading="loading"
  32. class="is-active"
  33. size="default"
  34. type="primary"
  35. @click.prevent="handleLogin"
  36. >登录</el-button
  37. >
  38. <el-button v-else :loading="loading" size="default" type="primary">登录</el-button>
  39. <el-checkbox v-model="autoLogin" label="自动登录" fill="#006eff" text-color="#737373" size="small" />
  40. </el-form>
  41. </div>
  42. </template>
  43. <script setup lang="ts">
  44. import router from "@/router";
  45. import SvgIcon from "@/components/SvgIcon/index.vue";
  46. // 状态管理依赖
  47. import {useUserStore} from "@/store/modules/user";
  48. // API依赖
  49. import {LocationQuery, LocationQueryValue, useRoute} from "vue-router";
  50. import {LoginData} from "@/api/auth/types";
  51. import {useWindowSize} from "@vueuse/core";
  52. import {computed} from "vue";
  53. const {width} = useWindowSize();
  54. const userStore = useUserStore();
  55. const route = useRoute();
  56. const classObj = computed(() => ({
  57. pad: width.value >= 768 && width.value < 1080,
  58. mobile: width.value < 768,
  59. }));
  60. /**
  61. * 按钮loading
  62. */
  63. const loading = ref(false);
  64. /**
  65. * 密码是否可见
  66. */
  67. const passwordVisible = ref(false);
  68. /**
  69. * 登录表单引用
  70. */
  71. const loginFormRef = ref(ElForm);
  72. const autoLogin = ref(!!localStorage.getItem("autoName"));
  73. const loginData = ref<LoginData>({
  74. // phone: "18770033942",
  75. // password: "123456",
  76. phone: atob(localStorage.getItem("autoName") || ""),
  77. password: atob(localStorage.getItem("autoPass") || ""),
  78. });
  79. const loginRules = {
  80. phone: [{required: true, trigger: "blur", validator: usernameValidator}],
  81. password: [{required: true, trigger: "blur", validator: passwordValidator}],
  82. };
  83. /**
  84. * 用户名校验
  85. */
  86. function usernameValidator(rule: any, value: any, callback: any) {
  87. if (value * 1 === 0) {
  88. callback(new Error("输入的手机号码不能为空"));
  89. }
  90. if (!new RegExp(/^1[3-9][0-9]{9}$/).test(value)) {
  91. callback(new Error("输入的手机号码格式错误"));
  92. }
  93. callback();
  94. }
  95. /**
  96. * 密码校验器
  97. */
  98. function passwordValidator(rule: any, value: any, callback: any) {
  99. if (value.length < 6) {
  100. callback(new Error("密码格式错误"));
  101. } else {
  102. callback();
  103. }
  104. }
  105. /**
  106. * 登录
  107. */
  108. function handleLogin() {
  109. loginFormRef.value.validate((valid: boolean) => {
  110. if (valid) {
  111. loading.value = true;
  112. if (autoLogin.value) {
  113. // 自动登录存入本地存储
  114. localStorage.setItem("autoName", btoa(<string>loginData.value.phone));
  115. localStorage.setItem("autoPass", btoa(<string>loginData.value.password));
  116. }
  117. userStore
  118. .login(loginData.value)
  119. .then(() => {
  120. const query: LocationQuery = route.query;
  121. const redirect = (query.redirect as LocationQueryValue) ?? "/";
  122. const otherQueryParams = Object.keys(query).reduce((acc: any, cur: string) => {
  123. if (cur !== "redirect") {
  124. acc[cur] = query[cur];
  125. }
  126. return acc;
  127. }, {});
  128. if (!autoLogin.value) {
  129. localStorage.removeItem("autoName");
  130. localStorage.removeItem("autoPass");
  131. }
  132. router.push({path: redirect, query: otherQueryParams});
  133. })
  134. .catch((error) => {
  135. console.log("登录", error);
  136. //ElMessage.error(error.message || "您输入的账号或密码错误!");
  137. // 验证失败,重新生成验证码
  138. new Error("您输入的账号或密码错误");
  139. })
  140. .finally(() => {
  141. loading.value = false;
  142. });
  143. }
  144. });
  145. }
  146. </script>
  147. <style lang="scss" scoped>
  148. .login-container {
  149. box-sizing: border-box;
  150. width: 100%;
  151. height: 100%;
  152. min-height: 750px;
  153. padding-top: 260px;
  154. padding-left: 660px;
  155. overflow: hidden;
  156. background: #eaf7fd url("../../assets/login/login.jpg") no-repeat left top;
  157. background-size: auto 100%;
  158. .login-form {
  159. width: 500px;
  160. max-width: 100%;
  161. padding: 0 42px 20px;
  162. margin: 0 auto;
  163. overflow: hidden;
  164. background: #fff;
  165. box-shadow: 0 0 10px #f2f3f5;
  166. .title {
  167. height: 98px;
  168. font-size: 30px;
  169. line-height: 98px;
  170. color: #151515;
  171. }
  172. .svg-icon {
  173. width: 18px;
  174. height: 18px;
  175. }
  176. }
  177. .el-form-item {
  178. height: 54px;
  179. margin-bottom: 40px;
  180. font-size: 16px;
  181. line-height: 54px;
  182. color: #747474;
  183. background: #f2f3f5;
  184. border: 1px solid #f2f3f5;
  185. border-radius: 5px;
  186. }
  187. .el-button {
  188. width: 100%;
  189. height: 54px;
  190. margin: 20px auto 10px;
  191. font-size: 18px;
  192. line-height: 54px;
  193. background: #ddd;
  194. border: none;
  195. &.is-active {
  196. background: #006eff;
  197. }
  198. }
  199. &.pad {
  200. padding: 400px 0;
  201. background-position: center center;
  202. }
  203. &.mobile {
  204. box-sizing: border-box;
  205. width: 750px;
  206. height: 1080px;
  207. padding: 420px 0 0;
  208. background-size: cover;
  209. .login-form {
  210. margin: 0 auto;
  211. .title {
  212. height: 80px;
  213. line-height: 80px;
  214. }
  215. }
  216. .el-form-item {
  217. margin-bottom: 20px;
  218. }
  219. }
  220. }
  221. .el-input {
  222. height: 54px;
  223. line-height: 54px;
  224. background: transparent;
  225. // 子组件 scoped 无效,使用 :deep
  226. :deep(.el-input__wrapper) {
  227. height: 54px;
  228. padding: 0;
  229. background: transparent;
  230. box-shadow: none;
  231. .el-input__inner {
  232. height: 54px;
  233. text-indent: 1.5em;
  234. background: transparent;
  235. border: 0;
  236. border-radius: 0;
  237. &:-webkit-autofill {
  238. box-shadow: 0 0 0 1000px transparent inset !important;
  239. -webkit-text-fill-color: #fff !important;
  240. }
  241. // 设置输入框自动填充的延迟属性
  242. &:-webkit-autofill,
  243. &:-webkit-autofill:hover,
  244. &:-webkit-autofill:focus,
  245. &:-webkit-autofill:active {
  246. transition: color 99999s ease-out, background-color 99999s ease-out;
  247. transition-delay: 99999s;
  248. }
  249. }
  250. }
  251. }
  252. :deep(.el-checkbox__input .el-checkbox__inner) {
  253. color: #727272;
  254. border-color: #727272;
  255. }
  256. :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
  257. background-color: #006eff;
  258. border-color: #006eff;
  259. }
  260. :deep(.el-checkbox__input.is-checked + .el-checkbox__label) {
  261. color: #727272;
  262. }
  263. </style>