login.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. <!--
  2. * @Author: qianlishi
  3. * @Date: 2020-07-13 11:04:24
  4. * @Last Modified by: qianlishi
  5. * @Last Modified time: 2020-07-13 11:04:24
  6. !-->
  7. <template>
  8. <div class="login_container">
  9. <!-- 顶部logo -->
  10. <div class="login_title">
  11. <img src="@/assets/images/home-logo.png"
  12. alt="logo" />
  13. </div>
  14. <div class="login_contant">
  15. <img src="@/assets/images/login.png"
  16. alt="image"
  17. class="login_img" />
  18. <el-form ref="loginForm"
  19. :model="loginForm"
  20. :rules="loginRules"
  21. class="login_form"
  22. autocomplete="on"
  23. label-position="left"
  24. @keyup.enter.native="handleLogin">
  25. <div class="title_container">
  26. <h3 class="title">
  27. HELLO,
  28. <br />
  29. <p class="title_name">在线大屏</p>
  30. </h3>
  31. </div>
  32. <div class="form_fields">
  33. <!-- 黄色条条 -->
  34. <i :style="{ top: activeTop + '%' }" />
  35. <!-- 中间条条 -->
  36. <b />
  37. <div>
  38. <p>用户名</p>
  39. <el-form-item prop="loginName">
  40. <el-input ref="loginName"
  41. v-model="loginForm.loginName"
  42. placeholder="用户名"
  43. name="loginName"
  44. type="text"
  45. tabindex="1"
  46. autocomplete="on"
  47. @focus="setTop('0')"
  48. @change="getPsw" />
  49. </el-form-item>
  50. </div>
  51. <div>
  52. <p>密码</p>
  53. <input name="password"
  54. type="password"
  55. autocomplete="off"
  56. class="take" />
  57. <el-tooltip v-model="capsTooltip"
  58. content="Caps lock is On"
  59. placement="right"
  60. manual>
  61. <el-form-item prop="password">
  62. <el-input :key="passwordType"
  63. ref="password"
  64. v-model="loginForm.password"
  65. :type="passwordType"
  66. placeholder="用户密码"
  67. name="password"
  68. tabindex="2"
  69. autocomplete="on"
  70. @blur="capsTooltip = false"
  71. @focus="setTop('50')"
  72. @keyup.native="checkCapslock" />
  73. <span class="show_pwd"
  74. @click="showPwd">
  75. <i class="el-icon-view" />
  76. </span>
  77. </el-form-item>
  78. </el-tooltip>
  79. </div>
  80. </div>
  81. <div class="control">
  82. <div class="remember">
  83. <input v-model="rememberPsw"
  84. type="checkbox" />
  85. <p>记住密码</p>
  86. </div>
  87. </div>
  88. <el-button :loading="loading"
  89. type="primary"
  90. class="login_btn"
  91. @click.native.prevent="handleLogin">登录</el-button>
  92. </el-form>
  93. </div>
  94. <!-- 验证码 -->
  95. <Verify v-if="needCaptcha"
  96. ref="verify"
  97. :captcha-type="'blockPuzzle'"
  98. :img-size="{ width: '400px', height: '200px' }"
  99. @success="verifylogin" />
  100. </div>
  101. </template>
  102. <script>
  103. import Verify from '@/components/verifition/Verify'
  104. import cookies from 'js-cookie'
  105. import { Decrypt, Encrypt } from '@/utils/index'
  106. import { login } from '@/api/login'
  107. import { transPsw } from '@/utils/encrypted'
  108. import { setToken, setAccessUser } from "@/utils/auth";
  109. export default {
  110. name: 'Login',
  111. components: {
  112. Verify,
  113. },
  114. data () {
  115. return {
  116. activeTop: '-50%', // 色条滑块控制
  117. rememberPsw: false, // 记住密码选择框
  118. loginForm: {
  119. loginName: 'admin',
  120. password: '123456',
  121. verifyCode: '',
  122. }, // 登录表单
  123. loginRules: {
  124. loginName: [{ required: true, message: '用户名必填', trigger: 'blur' }],
  125. password: [
  126. { required: true, message: '用户密码必填', trigger: 'blur' },
  127. ],
  128. }, // 登录表单验证
  129. passwordType: 'password', // 用来控制查看密码操作时的输入框类型
  130. capsTooltip: false, // 键盘大写是否打开
  131. loading: false, // 登录loding
  132. redirect: undefined, // 记录重定向地址
  133. otherQuery: {}, // 记录重定向地址中的参数
  134. needCaptcha: false,
  135. }
  136. },
  137. watch: {
  138. $route: {
  139. // 监听路由获取上个路由(from)的地址和参数
  140. handler: function (route) {
  141. const query = route.query
  142. if (query) {
  143. this.redirect = query.redirect
  144. this.otherQuery = this.getOtherQuery(query)
  145. }
  146. },
  147. immediate: true,
  148. },
  149. },
  150. mounted () {
  151. // 获取焦点
  152. if (this.loginForm.loginName === '') {
  153. this.$refs.loginName.focus()
  154. } else if (this.loginForm.password === '') {
  155. this.$refs.password.focus()
  156. }
  157. },
  158. methods: {
  159. // 获取存储的密码并解密
  160. getPsw () {
  161. const cookVal = cookies.get(`u_${this.loginForm.loginName}`)
  162. this.loginForm.password = cookVal && Decrypt(cookVal)
  163. },
  164. // 滑动条块的top控制
  165. setTop (val) {
  166. this.activeTop = val
  167. },
  168. // 检测大写锁定键是否开启
  169. checkCapslock (e) {
  170. const { key } = e
  171. this.capsTooltip = key && key.length === 1 && key >= 'A' && key <= 'Z'
  172. },
  173. // 查看密码
  174. showPwd () {
  175. if (this.passwordType === 'password') {
  176. this.passwordType = ''
  177. } else {
  178. this.passwordType = 'password'
  179. }
  180. this.$nextTick(() => {
  181. this.$refs.password.focus()
  182. })
  183. },
  184. // 滑动验证码
  185. useVerify () {
  186. this.$refs.loginForm.validate((valid) => {
  187. if (valid) {
  188. this.$refs.verify.show()
  189. } else {
  190. return false
  191. }
  192. })
  193. },
  194. // 验证成功的回调
  195. verifylogin (params) {
  196. this.loginForm.verifyCode = params.captchaVerification
  197. if (this.loginForm.verifyCode) {
  198. this.loginApi()
  199. }
  200. },
  201. // 登录操作
  202. handleLogin () {
  203. this.$refs.loginForm.validate((valid) => {
  204. if (valid) {
  205. this.loading = true
  206. // 登录失败次数过多需要展示滑动验证码
  207. if (this.needCaptcha) {
  208. this.useVerify()
  209. return
  210. }
  211. this.loginApi()
  212. } else {
  213. return false
  214. }
  215. })
  216. },
  217. async loginApi () {
  218. let obj = {
  219. loginName: this.loginForm.loginName,
  220. password: transPsw(this.loginForm.password),
  221. verifyCode: '',
  222. }
  223. const { code, data } = await login(obj)
  224. console.log(data)
  225. this.loading = false
  226. if (code != '200') return
  227. setToken(data.token)
  228. setAccessUser(data)
  229. // 选中记住密码时 把密码存到cookie里,时效15天
  230. this.rememberPsw &&
  231. cookies.set(
  232. `u_${this.loginForm.loginName}`,
  233. Encrypt(this.loginForm.password),
  234. { expires: 15 }
  235. )
  236. if (data && data.captcha) {
  237. this.needCaptcha = true
  238. } else {
  239. this.needCaptcha = false
  240. this.$router.push({
  241. path: this.redirect || '/index',
  242. query: this.otherQuery,
  243. })
  244. }
  245. },
  246. getOtherQuery (query) {
  247. return Object.keys(query).reduce((acc, cur) => {
  248. if (cur !== 'redirect') {
  249. acc[cur] = query[cur]
  250. }
  251. return acc
  252. }, {})
  253. },
  254. },
  255. }
  256. </script>
  257. <style lang="scss">
  258. .login_container .el-input input {
  259. color: #000;
  260. background: #fff;
  261. }
  262. /* reset element-ui css */
  263. .login_container {
  264. .el-input {
  265. display: inline-block;
  266. width: 100%;
  267. input {
  268. -webkit-appearance: none;
  269. caret-color: rgba($color: #000000, $alpha: 0.3);
  270. border: 1px solid #fff;
  271. &:-webkit-autofill {
  272. box-shadow: 0 0 0px 1000px #eee inset !important;
  273. -webkit-text-fill-color: #666 !important;
  274. }
  275. }
  276. }
  277. .el-form-item {
  278. border-radius: 5px;
  279. color: #454545;
  280. }
  281. }
  282. .verifybox {
  283. position: absolute;
  284. left: auto;
  285. right: 30%;
  286. transform: translate(50%, -50%);
  287. }
  288. </style>
  289. <style lang="scss" scoped>
  290. .take {
  291. position: absolute;
  292. top: 0;
  293. left: 0;
  294. z-index: -1;
  295. }
  296. .login_container {
  297. height: 100%;
  298. width: 100%;
  299. overflow: hidden;
  300. .login_title {
  301. width: 100%;
  302. height: 60px;
  303. padding: 10px 60px;
  304. display: flex;
  305. align-items: center;
  306. img {
  307. width: 10%;
  308. display: block;
  309. }
  310. }
  311. .login_contant {
  312. position: relative;
  313. width: 100%;
  314. height: calc(100% - 60px);
  315. // height: 100%;
  316. .login_img {
  317. display: block;
  318. width: 100%;
  319. height: 100%;
  320. }
  321. .login_form {
  322. position: absolute;
  323. top: 50%;
  324. right: 30%;
  325. transform: translate(50%, -50%);
  326. min-width: 400px;
  327. width: 22%;
  328. height: 460px;
  329. background-color: #ffffff;
  330. border-radius: 11px;
  331. padding: 30px;
  332. overflow: hidden;
  333. .title_container {
  334. position: relative;
  335. .title {
  336. font-size: 24px;
  337. color: #1a1a1a;
  338. .title_name {
  339. margin: 0;
  340. font-size: 18px;
  341. }
  342. }
  343. .set_language {
  344. color: #fff;
  345. position: absolute;
  346. top: 3px;
  347. font-size: 18px;
  348. right: 0px;
  349. cursor: pointer;
  350. }
  351. }
  352. .form_fields {
  353. position: relative;
  354. width: 100%;
  355. overflow: hidden;
  356. padding: 5px 16px;
  357. background: #ffffff;
  358. border: 1px solid #e0e0e0;
  359. box-shadow: 0 0 14px 4px rgba(230, 229, 229, 0.5);
  360. border-radius: 4px 10px 10px 4px;
  361. i {
  362. position: absolute;
  363. top: -50%;
  364. left: 0;
  365. width: 4px;
  366. height: 50%;
  367. transition: top 0.2s;
  368. background: #f5ab1b;
  369. border-radius: 14px;
  370. }
  371. b {
  372. position: absolute;
  373. top: 50%;
  374. left: 0;
  375. width: 100%;
  376. height: 1px;
  377. background: #e0e0e0;
  378. border-radius: 2px;
  379. margin-top: -0.5px;
  380. }
  381. p {
  382. margin: 0;
  383. padding: 0;
  384. line-height: 32px;
  385. height: 32px;
  386. font-size: 12px;
  387. color: #666;
  388. }
  389. .show_pwd {
  390. position: absolute;
  391. right: 10px;
  392. top: 0;
  393. font-size: 16px;
  394. color: #889aa4;
  395. cursor: pointer;
  396. user-select: none;
  397. }
  398. }
  399. .control {
  400. width: 100%;
  401. height: 70px;
  402. display: flex;
  403. align-items: center;
  404. justify-content: space-between;
  405. font-size: 14px;
  406. color: #919191;
  407. .remember {
  408. width: 36%;
  409. display: flex;
  410. align-items: center;
  411. p {
  412. padding-left: 8px;
  413. }
  414. // justify-content: space-between;
  415. & > input {
  416. position: relative;
  417. width: 14px;
  418. height: 14px;
  419. }
  420. & > input:checked::before {
  421. content: "\2713";
  422. background-color: #f5ab1b;
  423. position: absolute;
  424. top: 0;
  425. left: 0px;
  426. padding-left: 1.5px;
  427. width: 100%;
  428. height: 100%;
  429. border: 1px solid #f5ab1b;
  430. border-radius: 2px;
  431. font-size: 12px;
  432. color: white;
  433. font-weight: bold;
  434. }
  435. }
  436. }
  437. .login_btn {
  438. width: 130px;
  439. height: 40px;
  440. background: #f5ab1b;
  441. border: none;
  442. border-radius: 10px;
  443. font-size: 16px;
  444. color: #ffffff;
  445. text-align: center;
  446. }
  447. }
  448. }
  449. }
  450. </style>