右旗智慧驼厂
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

436 lines
11 KiB

  1. <template>
  2. <div class="login">
  3. <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
  4. <!-- 品牌区域 -->
  5. <div class="brand-area">
  6. <div class="logo-icon">🐫</div>
  7. <h3 class="title">{{ title }}</h3>
  8. <p class="subtitle">智慧牧场 · 轻松管理</p>
  9. </div>
  10. <el-form-item prop="username">
  11. <el-input
  12. v-model="loginForm.username"
  13. type="text"
  14. auto-complete="off"
  15. placeholder="账号 / 手机号"
  16. class="custom-input"
  17. >
  18. <svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
  19. </el-input>
  20. </el-form-item>
  21. <el-form-item prop="password">
  22. <el-input
  23. v-model="loginForm.password"
  24. type="password"
  25. auto-complete="off"
  26. placeholder="密码"
  27. @keyup.enter.native="handleLogin"
  28. class="custom-input"
  29. >
  30. <svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
  31. </el-input>
  32. </el-form-item>
  33. <!-- 优化后的验证码区域更紧凑对齐美观 -->
  34. <el-form-item prop="code" v-if="captchaEnabled" class="captcha-item">
  35. <div class="captcha-wrapper">
  36. <el-input
  37. v-model="loginForm.code"
  38. auto-complete="off"
  39. placeholder="验证码"
  40. @keyup.enter.native="handleLogin"
  41. class="captcha-input"
  42. >
  43. <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
  44. </el-input>
  45. <div class="captcha-image" @click="getCode">
  46. <img :src="codeUrl" class="captcha-img" alt="验证码" />
  47. <span class="refresh-icon" title="点击刷新"></span>
  48. </div>
  49. </div>
  50. </el-form-item>
  51. <!-- 选项栏记住密码 + 立即注册 -->
  52. <div class="options-bar">
  53. <el-checkbox v-model="loginForm.rememberMe" class="remember-checkbox">记住密码</el-checkbox>
  54. <router-link v-if="register" class="register-link" to="/register">立即注册</router-link>
  55. </div>
  56. <el-form-item style="width:100%;">
  57. <el-button
  58. :loading="loading"
  59. size="medium"
  60. type="primary"
  61. class="login-btn"
  62. @click.native.prevent="handleLogin"
  63. >
  64. <span v-if="!loading"> </span>
  65. <span v-else> 中...</span>
  66. </el-button>
  67. </el-form-item>
  68. </el-form>
  69. </div>
  70. </template>
  71. <script>
  72. import { getCodeImg } from "@/api/login"
  73. import Cookies from "js-cookie"
  74. import { encrypt, decrypt } from '@/utils/jsencrypt'
  75. import defaultSettings from '@/settings'
  76. export default {
  77. name: "Login",
  78. data() {
  79. return {
  80. title: '阿右旗阿拉腾敖包镇骆驼产业标准化智慧化示范基地数据中心',
  81. footerContent: defaultSettings.footerContent,
  82. codeUrl: "",
  83. loginForm: {
  84. username: "admin",
  85. password: "admin123",
  86. rememberMe: false,
  87. code: "",
  88. uuid: ""
  89. },
  90. loginRules: {
  91. username: [
  92. { required: true, trigger: "blur", message: "请输入您的账号" }
  93. ],
  94. password: [
  95. { required: true, trigger: "blur", message: "请输入您的密码" }
  96. ],
  97. code: [{ required: true, trigger: "change", message: "请输入验证码" }]
  98. },
  99. loading: false,
  100. captchaEnabled: true,
  101. register: false,
  102. redirect: undefined,
  103. showDemoTip: true
  104. }
  105. },
  106. watch: {
  107. $route: {
  108. handler: function(route) {
  109. this.redirect = route.query && route.query.redirect
  110. },
  111. immediate: true
  112. }
  113. },
  114. created() {
  115. this.getCode()
  116. this.getCookie()
  117. },
  118. methods: {
  119. getCode() {
  120. getCodeImg().then(res => {
  121. this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled
  122. if (this.captchaEnabled) {
  123. this.codeUrl = "data:image/gif;base64," + res.img
  124. this.loginForm.uuid = res.uuid
  125. }
  126. })
  127. },
  128. getCookie() {
  129. const username = Cookies.get("username")
  130. const password = Cookies.get("password")
  131. const rememberMe = Cookies.get('rememberMe')
  132. this.loginForm = {
  133. username: username === undefined ? this.loginForm.username : username,
  134. password: password === undefined ? this.loginForm.password : decrypt(password),
  135. rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
  136. }
  137. },
  138. handleLogin() {
  139. this.$refs.loginForm.validate(valid => {
  140. if (valid) {
  141. this.loading = true
  142. if (this.loginForm.rememberMe) {
  143. Cookies.set("username", this.loginForm.username, { expires: 30 })
  144. Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 })
  145. Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 })
  146. } else {
  147. Cookies.remove("username")
  148. Cookies.remove("password")
  149. Cookies.remove('rememberMe')
  150. }
  151. this.$store.dispatch("Login", this.loginForm).then(() => {
  152. this.$router.push({ path: "/Home" }).catch(() => { });
  153. }).catch(() => {
  154. this.loading = false
  155. if (this.captchaEnabled) {
  156. this.getCode()
  157. }
  158. })
  159. }
  160. })
  161. }
  162. }
  163. }
  164. </script>
  165. <style rel="stylesheet/scss" lang="scss" scoped>
  166. // 颜色变量 - 驼场主题色(自然、大地、清新)
  167. $primary-color: #7c9a3e; // 草绿色主色调
  168. $primary-hover: #5c7a2e; // 深草绿
  169. $bg-overlay: rgba(0, 0, 0, 0.3);
  170. $text-dark: #2c3a1f;
  171. $text-light: #6b7a5a;
  172. $card-white: rgba(255, 255, 255, 0.96);
  173. $shadow-sm: 0 20px 35px -10px rgba(0, 0, 0, 0.2);
  174. $shadow-focus: 0 0 0 3px rgba(124, 154, 62, 0.2);
  175. .login {
  176. position: relative;
  177. display: flex;
  178. justify-content: center;
  179. align-items: center;
  180. height: 100vh;
  181. min-height: 500px;
  182. background: linear-gradient(135deg, #2b5e2b 0%, #8cb36b 50%, #d4c9a3 100%);
  183. overflow: hidden;
  184. // 登录卡片
  185. .login-form {
  186. position: relative;
  187. z-index: 2;
  188. width: 440px;
  189. padding: 40px 35px 35px;
  190. background: $card-white;
  191. backdrop-filter: blur(2px);
  192. border-radius: 32px;
  193. box-shadow: $shadow-sm;
  194. transition: all 0.3s ease;
  195. border: 1px solid rgba(255,255,255,0.5);
  196. &:hover {
  197. transform: translateY(-3px);
  198. box-shadow: 0 25px 40px -12px rgba(0, 0, 0, 0.25);
  199. }
  200. }
  201. // 品牌区
  202. .brand-area {
  203. text-align: center;
  204. margin-bottom: 30px;
  205. .logo-icon {
  206. font-size: 56px;
  207. display: inline-block;
  208. animation: gentleSwing 2s infinite ease;
  209. }
  210. .title {
  211. margin: 12px 0 6px;
  212. font-size: 28px;
  213. font-weight: 600;
  214. color: $text-dark;
  215. letter-spacing: 2px;
  216. }
  217. .subtitle {
  218. font-size: 13px;
  219. color: $text-light;
  220. margin-bottom: 5px;
  221. font-weight: 400;
  222. }
  223. }
  224. // 输入框样式优化
  225. .custom-input {
  226. :deep(.el-input__inner) {
  227. height: 48px;
  228. border-radius: 60px;
  229. border: 1px solid #e2e8e6;
  230. background: #fefef7;
  231. padding-left: 42px;
  232. font-size: 15px;
  233. transition: all 0.2s;
  234. &:focus {
  235. border-color: $primary-color;
  236. box-shadow: $shadow-focus;
  237. }
  238. }
  239. :deep(.el-input__prefix) {
  240. left: 16px;
  241. top: 2px;
  242. .input-icon {
  243. font-size: 18px;
  244. color: $primary-color;
  245. }
  246. }
  247. }
  248. // ========= 优化后的验证码样式 =========
  249. .captcha-item {
  250. margin-bottom: 18px;
  251. :deep(.el-form-item__content) {
  252. line-height: initial;
  253. }
  254. }
  255. .captcha-wrapper {
  256. display: flex;
  257. align-items: center;
  258. gap: 12px;
  259. width: 100%;
  260. }
  261. .captcha-input {
  262. flex: 1;
  263. :deep(.el-input__inner) {
  264. height: 48px;
  265. border-radius: 60px;
  266. border: 1px solid #e2e8e6;
  267. background: #fefef7;
  268. padding-left: 42px;
  269. font-size: 15px;
  270. transition: all 0.2s;
  271. &:focus {
  272. border-color: $primary-color;
  273. box-shadow: $shadow-focus;
  274. }
  275. }
  276. :deep(.el-input__prefix) {
  277. left: 16px;
  278. top: 2px;
  279. .input-icon {
  280. font-size: 18px;
  281. color: $primary-color;
  282. }
  283. }
  284. }
  285. .captcha-image {
  286. position: relative;
  287. flex-shrink: 0;
  288. width: 110px;
  289. height: 48px;
  290. border-radius: 12px;
  291. overflow: hidden;
  292. cursor: pointer;
  293. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  294. transition: transform 0.2s, box-shadow 0.2s;
  295. &:hover {
  296. transform: scale(1.02);
  297. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  298. .refresh-icon {
  299. opacity: 1;
  300. }
  301. }
  302. }
  303. .captcha-img {
  304. width: 100%;
  305. height: 100%;
  306. object-fit: cover;
  307. display: block;
  308. }
  309. .refresh-icon {
  310. position: absolute;
  311. top: 50%;
  312. left: 50%;
  313. transform: translate(-50%, -50%);
  314. font-size: 22px;
  315. font-weight: bold;
  316. color: white;
  317. text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  318. opacity: 0;
  319. transition: opacity 0.2s;
  320. background: rgba(0, 0, 0, 0.5);
  321. width: 32px;
  322. height: 32px;
  323. border-radius: 50%;
  324. display: flex;
  325. align-items: center;
  326. justify-content: center;
  327. backdrop-filter: blur(2px);
  328. pointer-events: none;
  329. }
  330. // 选项栏(记住密码 + 注册)
  331. .options-bar {
  332. display: flex;
  333. justify-content: space-between;
  334. align-items: center;
  335. margin: 8px 0 25px 0;
  336. .remember-checkbox {
  337. :deep(.el-checkbox__inner) {
  338. border-radius: 4px;
  339. border-color: #cbd5e1;
  340. background-color: #fff;
  341. }
  342. :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
  343. background-color: $primary-color;
  344. border-color: $primary-color;
  345. }
  346. :deep(.el-checkbox__label) {
  347. color: $text-light;
  348. font-size: 13px;
  349. }
  350. }
  351. .register-link {
  352. color: $primary-color;
  353. font-size: 13px;
  354. text-decoration: none;
  355. font-weight: 500;
  356. transition: 0.2s;
  357. &:hover {
  358. color: $primary-hover;
  359. text-decoration: underline;
  360. }
  361. }
  362. }
  363. // 登录按钮
  364. .login-btn {
  365. width: 100%;
  366. height: 48px;
  367. background: $primary-color;
  368. border: none;
  369. border-radius: 60px;
  370. font-size: 16px;
  371. font-weight: 600;
  372. letter-spacing: 2px;
  373. box-shadow: 0 8px 18px rgba(124, 154, 62, 0.3);
  374. transition: all 0.25s;
  375. &:hover, &:focus {
  376. background: $primary-hover;
  377. transform: scale(1.01);
  378. box-shadow: 0 10px 22px rgba(92, 122, 46, 0.4);
  379. }
  380. }
  381. }
  382. // 动画
  383. @keyframes gentleSwing {
  384. 0%,100%{ transform: rotate(0deg); }
  385. 50%{ transform: rotate(6deg); }
  386. }
  387. // 响应式
  388. @media (max-width: 500px) {
  389. .login .login-form {
  390. width: 88%;
  391. padding: 30px 20px;
  392. }
  393. .captcha-wrapper {
  394. gap: 8px;
  395. }
  396. .captcha-image {
  397. width: 95px;
  398. height: 44px;
  399. }
  400. .captcha-input :deep(.el-input__inner) {
  401. height: 44px;
  402. }
  403. }
  404. </style>