index.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <template>
  2. <div class="login-container">
  3. <div class="login-card">
  4. <!-- 页头 Logo -->
  5. <div class="login-header">
  6. <i class="el-icon-cpu logo-icon"></i>
  7. <h2>设备管理系统</h2>
  8. <p>后台管理平台</p>
  9. </div>
  10. <!-- 登录表单 -->
  11. <el-form
  12. ref="loginForm"
  13. :model="loginForm"
  14. :rules="loginRules"
  15. class="login-form"
  16. @keyup.enter.native="handleLogin"
  17. >
  18. <el-form-item prop="username">
  19. <el-input
  20. v-model="loginForm.username"
  21. prefix-icon="el-icon-user"
  22. placeholder="用户名"
  23. clearable
  24. />
  25. </el-form-item>
  26. <el-form-item prop="password">
  27. <el-input
  28. v-model="loginForm.password"
  29. prefix-icon="el-icon-lock"
  30. placeholder="密码"
  31. type="password"
  32. show-password
  33. clearable
  34. />
  35. </el-form-item>
  36. <el-form-item prop="captcha">
  37. <el-row :gutter="10">
  38. <el-col :span="16">
  39. <el-input
  40. v-model="loginForm.captcha"
  41. prefix-icon="el-icon-key"
  42. placeholder="请输入验证码"
  43. />
  44. </el-col>
  45. <el-col :span="8">
  46. <div class="captcha-wrap" @click="loadCaptcha" title="点击刷新验证码">
  47. <img
  48. v-if="captchaImage"
  49. :src="captchaImage"
  50. class="captcha-img"
  51. alt="验证码"
  52. />
  53. <span v-else class="captcha-placeholder">加载中...</span>
  54. </div>
  55. </el-col>
  56. </el-row>
  57. </el-form-item>
  58. <el-form-item>
  59. <el-button
  60. type="primary"
  61. class="login-btn"
  62. :loading="loading"
  63. @click="handleLogin"
  64. >
  65. {{ loading ? '登录中...' : '登 录' }}
  66. </el-button>
  67. </el-form-item>
  68. </el-form>
  69. </div>
  70. </div>
  71. </template>
  72. <script>
  73. import md5 from 'js-md5'
  74. import { getCaptcha } from '@/api/auth'
  75. export default {
  76. name: 'Login',
  77. data() {
  78. return {
  79. loginForm: {
  80. username: '',
  81. password: '',
  82. captcha: ''
  83. },
  84. captchaKey: '', // 后端返回的验证码 key
  85. captchaImage: '', // Base64 验证码图片
  86. loading: false,
  87. loginRules: {
  88. username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
  89. password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
  90. captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }]
  91. }
  92. }
  93. },
  94. created() {
  95. this.loadCaptcha()
  96. },
  97. methods: {
  98. async loadCaptcha() {
  99. try {
  100. const res = await getCaptcha()
  101. this.captchaKey = res.data.captchaKey
  102. this.captchaImage = res.data.captchaImage
  103. this.loginForm.captcha = ''
  104. } catch {
  105. // 加载失败时保持旧图片
  106. }
  107. },
  108. handleLogin() {
  109. this.$refs.loginForm.validate(async valid => {
  110. if (!valid) return
  111. this.loading = true
  112. try {
  113. await this.$store.dispatch('user/login', {
  114. username: this.loginForm.username,
  115. password: md5(this.loginForm.password),
  116. captchaKey: this.captchaKey,
  117. captchaCode: this.loginForm.captcha
  118. })
  119. const redirect = this.$route.query.redirect || '/role'
  120. this.$router.push(redirect)
  121. this.$message.success('登录成功,欢迎回来!')
  122. } catch (err) {
  123. // 登录失败后刷新验证码
  124. this.loadCaptcha()
  125. this.$message.error(err.message || '登录失败,请重试')
  126. } finally {
  127. this.loading = false
  128. }
  129. })
  130. }
  131. }
  132. }
  133. </script>
  134. <style lang="scss" scoped>
  135. .login-container {
  136. width: 100%;
  137. height: 100vh;
  138. display: flex;
  139. align-items: center;
  140. justify-content: center;
  141. background: linear-gradient(135deg, #1a2a3a 0%, #304156 50%, #409eff 100%);
  142. .login-card {
  143. width: 420px;
  144. background: #fff;
  145. border-radius: 12px;
  146. padding: 40px;
  147. box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
  148. .login-header {
  149. text-align: center;
  150. margin-bottom: 32px;
  151. .logo-icon {
  152. font-size: 52px;
  153. color: #409eff;
  154. }
  155. h2 {
  156. margin: 12px 0 6px;
  157. font-size: 22px;
  158. color: #303133;
  159. font-weight: 600;
  160. }
  161. p {
  162. color: #909399;
  163. font-size: 13px;
  164. }
  165. }
  166. .login-form {
  167. .captcha-wrap {
  168. height: 40px;
  169. border: 1px solid #dcdfe6;
  170. border-radius: 4px;
  171. overflow: hidden;
  172. cursor: pointer;
  173. display: flex;
  174. align-items: center;
  175. justify-content: center;
  176. background: #f5f7fa;
  177. transition: border-color 0.2s;
  178. &:hover { border-color: #409eff; }
  179. .captcha-img {
  180. width: 100%;
  181. height: 100%;
  182. object-fit: cover;
  183. }
  184. .captcha-placeholder {
  185. font-size: 12px;
  186. color: #909399;
  187. }
  188. }
  189. .login-btn {
  190. width: 100%;
  191. height: 44px;
  192. font-size: 16px;
  193. letter-spacing: 4px;
  194. margin-top: 8px;
  195. }
  196. }
  197. }
  198. }
  199. </style>