index.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <script setup>
  2. import { ref, onMounted, onBeforeUnmount } from "vue"
  3. import { useRouter } from "vue-router"
  4. import { useUserStore } from "@/store/modules/user"
  5. import Logo from "@/assets/login/logo.svg?component"
  6. import Title from "@/assets/login/title.svg?component"
  7. import TitleLft from "@/assets/login/title-left.svg?component"
  8. import TitleRight from "@/assets/login/title-right.svg?component"
  9. import Name from "@/assets/login/name.svg?component"
  10. const router = useRouter()
  11. const isShow = ref(false)
  12. /** 登录表单元素的引用 */
  13. const loginFormRef = ref(null)
  14. /** 登录按钮 Loading */
  15. const loading = ref(false)
  16. /** 登录表单数据 */
  17. const loginFormData = ref({
  18. account: "",
  19. password: ""
  20. })
  21. /** 记住密码 */
  22. const rememberPassword = ref(false)
  23. /** 登录表单校验规则 */
  24. const loginFormRules = {
  25. account: [{ required: true, message: "请输入用户名", trigger: "blur" }],
  26. password: [
  27. { required: true, message: "请输入密码", trigger: "blur" },
  28. { min: 6, max: 16, message: "长度在 6 到 16 个字符", trigger: "blur" }
  29. ]
  30. }
  31. /** 登录逻辑 */
  32. const handleLogin = () => {
  33. loginFormRef.value?.validate((valid, fields) => {
  34. if (valid) {
  35. loading.value = true
  36. useUserStore()
  37. .login(loginFormData.value)
  38. .then(() => {
  39. if (rememberPassword.value) {
  40. localStorage.setItem("rememberPassword", true)
  41. localStorage.setItem("loginFormData", JSON.stringify(loginFormData.value))
  42. } else {
  43. localStorage.removeItem("rememberPassword")
  44. localStorage.removeItem("loginFormData")
  45. }
  46. router.push({ path: "/" })
  47. })
  48. .catch(() => {
  49. createCode()
  50. loginFormData.value.password = ""
  51. })
  52. .finally(() => {
  53. loading.value = false
  54. })
  55. } else {
  56. console.error("表单校验不通过", fields)
  57. }
  58. })
  59. }
  60. const preventDefaultActions = (e) => {
  61. e.preventDefault()
  62. }
  63. const preventZoom = (e) => {
  64. if (e.ctrlKey || e.metaKey) {
  65. e.preventDefault()
  66. }
  67. }
  68. const preventTextSelection = (e) => {
  69. e.preventDefault()
  70. }
  71. onMounted(() => {
  72. rememberPassword.value = localStorage.getItem("rememberPassword") ? true : false
  73. loginFormData.value = localStorage.getItem("loginFormData") ? JSON.parse(localStorage.getItem("loginFormData")) : {}
  74. document.addEventListener("contextmenu", preventDefaultActions)
  75. document.addEventListener("keydown", (e) => {
  76. if (e.ctrlKey && (e.key === "A" || e.key === "a")) {
  77. preventDefaultActions(e)
  78. }
  79. if (e.ctrlKey && (e.key === "=" || e.key === "-")) {
  80. preventDefaultActions(e)
  81. }
  82. if (e.ctrlKey && e.shiftKey && (e.key === "+" || e.key === "_")) {
  83. preventDefaultActions(e)
  84. }
  85. })
  86. document.addEventListener("wheel", preventZoom, { passive: false })
  87. document.addEventListener("mousedown", preventTextSelection)
  88. })
  89. onBeforeUnmount(() => {
  90. document.removeEventListener("contextmenu", preventDefaultActions)
  91. document.removeEventListener("keydown", preventDefaultActions)
  92. document.removeEventListener("wheel", preventZoom)
  93. document.removeEventListener("mousedown", preventTextSelection)
  94. })
  95. </script>
  96. <template>
  97. <Name class="name" />
  98. <div class="login-container">
  99. <div class="left">
  100. <div class="title">
  101. <TitleLft />
  102. <Title />
  103. <TitleRight />
  104. </div>
  105. <Logo class="logo" />
  106. </div>
  107. <div class="right">
  108. <div class="title">
  109. <span>账号登录</span>
  110. </div>
  111. <div class="content">
  112. <el-form ref="loginFormRef" :model="loginFormData" :rules="loginFormRules" @keyup.enter="handleLogin">
  113. <el-form-item prop="account">
  114. <el-input
  115. v-model.trim="loginFormData.account"
  116. placeholder="用户名"
  117. type="text"
  118. tabindex="1"
  119. size="large"
  120. clearable
  121. />
  122. </el-form-item>
  123. <el-form-item prop="password">
  124. <el-input
  125. v-model.trim="loginFormData.password"
  126. placeholder="密码"
  127. type="text"
  128. tabindex="2"
  129. size="large"
  130. clearable
  131. :class="!isShow ? 'no_autofill_pwd' : ''"
  132. />
  133. <div class="pwd_icon" @click="isShow = !isShow">
  134. <el-icon v-if="isShow" color="#a8abb2" size="22"><View /></el-icon>
  135. <el-icon v-else color="#a8abb2" size="22"><Hide /></el-icon>
  136. </div>
  137. </el-form-item>
  138. <el-checkbox v-model="rememberPassword" label="记住密码" />
  139. <el-button :loading="loading" type="primary" size="large" @click.prevent="handleLogin">登 录</el-button>
  140. </el-form>
  141. </div>
  142. </div>
  143. </div>
  144. </template>
  145. <style lang="scss" scoped>
  146. .name {
  147. position: fixed;
  148. left: 91px;
  149. top: 70px;
  150. }
  151. .el-form-item {
  152. margin-bottom: 31px;
  153. }
  154. .el-input {
  155. height: 64px;
  156. font-size: 20px;
  157. :deep(.el-input__password) {
  158. font-size: 20px;
  159. }
  160. }
  161. .el-checkbox {
  162. position: relative;
  163. top: -15px;
  164. :deep(.el-checkbox__input) {
  165. --el-checkbox-input-height: 20px;
  166. --el-checkbox-input-width: 20px;
  167. .el-checkbox__inner:after {
  168. height: 10px;
  169. left: 5.5px;
  170. top: 1.5px;
  171. width: 4px;
  172. }
  173. }
  174. :deep(.el-checkbox__label) {
  175. font-size: 20px;
  176. font-weight: 400;
  177. }
  178. }
  179. .login-container {
  180. overflow: auto;
  181. display: flex;
  182. justify-content: center;
  183. align-items: center;
  184. gap: 200px;
  185. min-height: 100%;
  186. background: linear-gradient(90deg, rgba(54, 111, 255, 1) 0%, rgba(93, 160, 252, 1) 100%);
  187. .left {
  188. display: flex;
  189. flex-direction: column;
  190. align-items: center;
  191. .title {
  192. display: flex;
  193. gap: 25px;
  194. }
  195. .logo {
  196. width: 592px;
  197. margin-top: 63px;
  198. }
  199. }
  200. .right {
  201. position: relative;
  202. left: 100px;
  203. min-width: 534px;
  204. border-radius: 10px;
  205. box-shadow: 0px 2px 16px rgba(92, 5, 5, 0.25);
  206. background-color: var(--el-bg-color);
  207. .title {
  208. display: flex;
  209. justify-content: center;
  210. margin-top: 68px;
  211. font-size: 32px;
  212. font-weight: 400;
  213. }
  214. .content {
  215. padding: 48px 40px 138px 40px;
  216. .el-button {
  217. width: 100%;
  218. height: 66px;
  219. margin-top: 80px;
  220. font-size: 28px;
  221. font-weight: 500;
  222. background: rgba(60, 120, 221, 1);
  223. }
  224. }
  225. }
  226. }
  227. .no_autofill_pwd {
  228. ::v-deep(.el-input__inner) {
  229. -webkit-text-security: disc !important;
  230. }
  231. }
  232. .pwd_icon {
  233. position: absolute;
  234. display: flex;
  235. justify-content: center;
  236. align-items: center;
  237. top: 12px;
  238. right: 5px;
  239. width: 40px;
  240. height: 40px;
  241. cursor: pointer;
  242. }
  243. ::v-deep(.el-input__wrapper) {
  244. padding-right: 50px;
  245. }
  246. ::v-deep(.el-input__suffix .el-input__clear) {
  247. font-size: 24px;
  248. }
  249. // 媒体查询 1680px
  250. @media screen and (max-width: 1680px) {
  251. .login-container {
  252. gap: 50px;
  253. }
  254. }
  255. // 媒体查询 1440px
  256. @media screen and (max-width: 1440px) {
  257. .left {
  258. display: none !important;
  259. }
  260. .right {
  261. left: 0 !important;
  262. }
  263. }
  264. </style>