Login.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <template>
  2. <div class="login-container">
  3. <div class="login-card">
  4. <div class="login-header">
  5. <h1>实验室安全智能监测与管控中心</h1>
  6. <p>中国安全生产科学研究院</p>
  7. </div>
  8. <el-form
  9. ref="loginForm"
  10. :model="loginForm"
  11. :rules="loginRules"
  12. class="login-form"
  13. >
  14. <el-form-item prop="account">
  15. <el-input
  16. v-model="loginForm.account"
  17. placeholder="请输入用户名/手机号"
  18. prefix-icon="el-icon-user"
  19. />
  20. </el-form-item>
  21. <el-form-item prop="password">
  22. <el-input
  23. v-model="loginForm.password"
  24. type="password"
  25. placeholder="请输入密码"
  26. prefix-icon="el-icon-lock"
  27. show-password
  28. @keyup.enter.native="handleLogin"
  29. />
  30. </el-form-item>
  31. <el-form-item prop="captcha">
  32. <div class="captcha-row">
  33. <el-input
  34. v-model="loginForm.captcha"
  35. placeholder="请输入验证码"
  36. prefix-icon="el-icon-key"
  37. />
  38. <div class="captcha-img" @click="refreshCaptcha">
  39. <img v-if="captchaImage" :src="captchaImage" />
  40. <span v-else>{{ captchaLoading ? '加载中…' : '点击刷新' }}</span>
  41. </div>
  42. </div>
  43. </el-form-item>
  44. <el-form-item>
  45. <el-button
  46. type="primary"
  47. :loading="loading"
  48. class="login-btn"
  49. @click="handleLogin"
  50. >
  51. 登 录
  52. </el-button>
  53. </el-form-item>
  54. </el-form>
  55. </div>
  56. </div>
  57. </template>
  58. <script>
  59. import md5 from 'js-md5'
  60. import { loginApi, getCaptcha } from '@/api/auth'
  61. export default {
  62. name: 'Login',
  63. data() {
  64. return {
  65. loginForm: {
  66. account: '',
  67. password: '',
  68. captcha: ''
  69. },
  70. loginRules: {
  71. account: [
  72. { required: true, message: '请输入用户名', trigger: 'blur' }
  73. ],
  74. password: [
  75. { required: true, message: '请输入密码', trigger: 'blur' }
  76. ],
  77. captcha: [
  78. { required: true, message: '请输入验证码', trigger: 'blur' }
  79. ]
  80. },
  81. loading: false,
  82. needCaptcha: true,
  83. captchaLoading: false,
  84. captchaImage: '',
  85. captchaUuid: ''
  86. }
  87. },
  88. created() {
  89. this.loadCaptcha()
  90. },
  91. methods: {
  92. async loadCaptcha() {
  93. this.captchaLoading = true
  94. this.captchaImage = ''
  95. try {
  96. const res = await getCaptcha()
  97. if (res.code === 200) {
  98. this.needCaptcha = res.data.captcha
  99. if (res.data.captcha) {
  100. this.captchaImage = 'data:image/jpeg;base64,' + res.data.image
  101. this.captchaUuid = res.data.uuid
  102. }
  103. }
  104. } catch (e) {
  105. // 失败保持"点击刷新"状态
  106. } finally {
  107. this.captchaLoading = false
  108. }
  109. },
  110. refreshCaptcha() {
  111. this.loadCaptcha()
  112. },
  113. handleLogin() {
  114. this.$refs.loginForm.validate(async (valid) => {
  115. if (!valid) return
  116. this.loading = true
  117. try {
  118. const res = await loginApi({
  119. account: this.loginForm.account,
  120. password: md5(this.loginForm.password),
  121. code: this.needCaptcha ? this.loginForm.captcha : undefined,
  122. uuid: this.needCaptcha ? this.captchaUuid : undefined
  123. })
  124. if (res.code === 200) {
  125. this.$store.dispatch('login', res.data.token)
  126. this.$message.success('登录成功')
  127. this.$router.push('/screen')
  128. } else {
  129. this.$message.error(res.message || '登录失败')
  130. if (this.needCaptcha) this.loadCaptcha()
  131. }
  132. } catch (err) {
  133. if (this.needCaptcha) this.loadCaptcha()
  134. } finally {
  135. this.loading = false
  136. }
  137. })
  138. }
  139. }
  140. }
  141. </script>
  142. <style lang="scss" scoped>
  143. .login-container {
  144. width: 100%;
  145. height: 100%;
  146. display: flex;
  147. align-items: center;
  148. justify-content: center;
  149. background: linear-gradient(135deg, #0a1a3a 0%, #0d2b5e 50%, #0a1a3a 100%);
  150. position: relative;
  151. &::before {
  152. content: '';
  153. position: absolute;
  154. top: 0;
  155. left: 0;
  156. right: 0;
  157. bottom: 0;
  158. background:
  159. radial-gradient(ellipse at 20% 50%, rgba(0, 100, 200, 0.15) 0%, transparent 50%),
  160. radial-gradient(ellipse at 80% 50%, rgba(0, 150, 255, 0.1) 0%, transparent 50%);
  161. pointer-events: none;
  162. }
  163. }
  164. .login-card {
  165. width: 480px;
  166. padding: 48px 40px;
  167. background: rgba(8, 30, 65, 0.9);
  168. border: 1px solid rgba(46, 120, 210, 0.4);
  169. border-radius: 12px;
  170. backdrop-filter: blur(10px);
  171. z-index: 1;
  172. }
  173. .login-header {
  174. text-align: center;
  175. margin-bottom: 40px;
  176. h1 {
  177. font-size: 24px;
  178. color: $cyan;
  179. margin-bottom: 8px;
  180. letter-spacing: 2px;
  181. }
  182. p {
  183. font-size: 14px;
  184. color: $text-dim;
  185. }
  186. }
  187. .login-form {
  188. ::v-deep .el-input__inner {
  189. background: rgba(6, 40, 90, 0.6);
  190. border: 1px solid rgba(46, 120, 210, 0.3);
  191. color: #fff;
  192. height: 44px;
  193. &::placeholder {
  194. color: rgba(140, 180, 230, 0.5);
  195. }
  196. &:focus {
  197. border-color: $cyan;
  198. }
  199. }
  200. ::v-deep .el-input__prefix {
  201. color: $cyan;
  202. }
  203. }
  204. .captcha-row {
  205. display: flex;
  206. gap: 12px;
  207. .el-input {
  208. flex: 1;
  209. }
  210. .captcha-img {
  211. width: 120px;
  212. height: 44px;
  213. flex-shrink: 0;
  214. background: rgba(6, 40, 90, 0.8);
  215. border: 1px solid rgba(46, 120, 210, 0.3);
  216. border-radius: 4px;
  217. display: flex;
  218. align-items: center;
  219. justify-content: center;
  220. cursor: pointer;
  221. user-select: none;
  222. overflow: hidden;
  223. color: $cyan;
  224. font-size: 13px;
  225. img {
  226. width: 100%;
  227. height: 100%;
  228. object-fit: contain;
  229. display: block;
  230. }
  231. }
  232. }
  233. .login-btn {
  234. width: 100%;
  235. height: 44px;
  236. font-size: 16px;
  237. background: linear-gradient(90deg, #1890ff, #00e5ff);
  238. border: none;
  239. letter-spacing: 8px;
  240. &:hover {
  241. opacity: 0.9;
  242. }
  243. }
  244. </style>