index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. <template>
  2. <div class="login-container">
  3. <el-form
  4. ref="loginForm"
  5. :model="loginForm"
  6. :rules="loginRules"
  7. class="login-form"
  8. autocomplete="on"
  9. label-position="left"
  10. >
  11. <lottie-web-cimo
  12. ref="lottie_web"
  13. animation-name="meeting"
  14. style="width: 85%; max-width: 95%; height: 12%; margin: auto"
  15. />
  16. <div class="title-container">
  17. <h3 class="title">机器人感知与信息融合小组管理系统</h3>
  18. </div>
  19. <el-form-item prop="username">
  20. <span class="svg-container">
  21. <svg-icon icon-class="user" />
  22. </span>
  23. <el-input
  24. ref="username"
  25. v-model="loginForm.username"
  26. placeholder=""
  27. name="username"
  28. type="text"
  29. tabindex="1"
  30. autocomplete="on"
  31. />
  32. </el-form-item>
  33. <el-tooltip
  34. v-model="capsTooltip"
  35. content="Caps lock is On"
  36. placement="right"
  37. manual
  38. >
  39. <el-form-item prop="password">
  40. <span class="svg-container">
  41. <svg-icon icon-class="password" />
  42. </span>
  43. <el-input
  44. :key="passwordType"
  45. ref="password"
  46. v-model="loginForm.password"
  47. :type="passwordType"
  48. placeholder="Password"
  49. name="password"
  50. tabindex="2"
  51. autocomplete="on"
  52. @keyup.native="checkCapslock"
  53. @blur="capsTooltip = false"
  54. @keyup.enter.native="handleLogin"
  55. />
  56. <span class="show-pwd" @click="showPwd">
  57. <svg-icon
  58. :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"
  59. />
  60. </span>
  61. </el-form-item>
  62. </el-tooltip>
  63. <el-button
  64. :loading="loading"
  65. type="primary"
  66. style="width: 100%; margin-bottom: 30px"
  67. @click.native.prevent="handleLogin"
  68. >登录组内系统</el-button>
  69. <div style="position: relative">
  70. <el-button type="primary" @click="registerDialogVisible = true">
  71. 没有账号?立即注册
  72. </el-button>
  73. <el-button type="primary" @click="showDialog = true">
  74. 使用第三方账号登录
  75. </el-button>
  76. </div>
  77. </el-form>
  78. <!-- 第三方登录弹窗 -->
  79. <el-dialog
  80. title="或者使用第三方账号登录"
  81. :visible.sync="showDialog"
  82. width="400px"
  83. class="register-dialog"
  84. >
  85. 后续使用微信或者qq扫码登录
  86. <br>
  87. <br>
  88. <br>
  89. <social-sign />
  90. </el-dialog>
  91. <!-- 注册弹窗 -->
  92. <el-dialog
  93. title="用户注册"
  94. :visible.sync="registerDialogVisible"
  95. width="400px"
  96. class="register-dialog"
  97. :before-close="handleClose"
  98. >
  99. <el-form
  100. ref="registerForm"
  101. :model="registerForm"
  102. :rules="registerRules"
  103. label-width="80px"
  104. >
  105. <el-form-item label="用户名" prop="username">
  106. <el-input
  107. v-model="registerForm.username"
  108. placeholder="请输入用户名"
  109. />
  110. </el-form-item>
  111. <el-form-item label="密码" prop="password">
  112. <el-input
  113. v-model="registerForm.password"
  114. :type="registerPasswordType"
  115. placeholder="请输入密码"
  116. >
  117. <i
  118. slot="suffix"
  119. :class="
  120. registerPasswordType === 'password'
  121. ? 'el-icon-view'
  122. : 'el-icon-view'
  123. "
  124. style="cursor: pointer; padding: 8px"
  125. @click="toggleRegisterPasswordVisibility"
  126. />
  127. </el-input>
  128. </el-form-item>
  129. <el-form-item label="确认密码" prop="confirmPassword">
  130. <el-input
  131. v-model="registerForm.confirmPassword"
  132. :type="confirmPasswordType"
  133. placeholder="请再次输入密码"
  134. >
  135. <i
  136. slot="suffix"
  137. :class="
  138. confirmPasswordType === 'password'
  139. ? 'el-icon-view'
  140. : 'el-icon-view'
  141. "
  142. style="cursor: pointer; padding: 8px"
  143. @click="toggleConfirmPasswordVisibility"
  144. />
  145. </el-input>
  146. </el-form-item>
  147. </el-form>
  148. <div slot="footer" class="dialog-footer">
  149. <el-button @click="registerDialogVisible = false">取 消</el-button>
  150. <el-button
  151. type="primary"
  152. :loading="registerLoading"
  153. @click="handleRegister"
  154. >注 册</el-button>
  155. </div>
  156. </el-dialog>
  157. </div>
  158. </template>
  159. <script>
  160. import SocialSign from './components/SocialSignin'
  161. import LottieWebCimo from '@/components/LottieWeb/lottie-web-cimo'
  162. import { post } from '@/boot/axios_request'
  163. export default {
  164. name: 'Login',
  165. components: { SocialSign, LottieWebCimo },
  166. data() {
  167. const validatePassword = (rule, value, callback) => {
  168. if (value.length < 6) {
  169. callback(new Error('密码长度不能少于6位'))
  170. } else {
  171. callback()
  172. }
  173. }
  174. const validateConfirmPassword = (rule, value, callback) => {
  175. if (value !== this.registerForm.password) {
  176. callback(new Error('两次输入的密码不一致'))
  177. } else {
  178. callback()
  179. }
  180. }
  181. return {
  182. loginForm: {
  183. username: '',
  184. password: ''
  185. },
  186. loginRules: {
  187. username: [
  188. { required: true, message: '请输入用户名', trigger: 'blur' },
  189. {
  190. min: 1,
  191. max: 15,
  192. message: '用户名长度在1到15个字符之间',
  193. trigger: 'blur'
  194. }
  195. ],
  196. password: [
  197. { required: true, message: '请输入密码', trigger: 'blur' },
  198. { validator: validatePassword, trigger: 'blur' }
  199. ]
  200. },
  201. // 注册相关数据
  202. registerDialogVisible: false,
  203. registerLoading: false,
  204. registerForm: {
  205. username: '',
  206. password: '',
  207. confirmPassword: ''
  208. },
  209. registerRules: {
  210. username: [
  211. { required: true, message: '请输入用户名', trigger: 'blur' },
  212. {
  213. min: 1,
  214. max: 15,
  215. message: '用户名长度在1到15个字符之间',
  216. trigger: 'blur'
  217. }
  218. ],
  219. password: [
  220. { required: true, message: '请输入密码', trigger: 'blur' },
  221. { validator: validatePassword, trigger: 'blur' }
  222. ],
  223. confirmPassword: [
  224. { required: true, message: '请确认密码', trigger: 'blur' },
  225. { validator: validateConfirmPassword, trigger: 'blur' }
  226. ]
  227. },
  228. registerPasswordType: 'password',
  229. confirmPasswordType: 'password',
  230. // 其他数据保持不变
  231. passwordType: 'password',
  232. capsTooltip: false,
  233. loading: false,
  234. showDialog: false,
  235. redirect: undefined,
  236. otherQuery: {}
  237. }
  238. },
  239. watch: {
  240. $route: {
  241. handler: function(route) {
  242. const query = route.query
  243. if (query) {
  244. this.redirect = query.redirect
  245. this.otherQuery = this.getOtherQuery(query)
  246. }
  247. },
  248. immediate: true
  249. }
  250. },
  251. created() {
  252. // window.addEventListener('storage', this.afterQRScan)
  253. },
  254. mounted() {
  255. if (this.loginForm.username === '') {
  256. this.$refs.username.focus()
  257. } else if (this.loginForm.password === '') {
  258. this.$refs.password.focus()
  259. }
  260. },
  261. destroyed() {
  262. // window.removeEventListener('storage', this.afterQRScan)
  263. },
  264. methods: {
  265. // 注册相关方法
  266. toggleRegisterPasswordVisibility() {
  267. this.registerPasswordType =
  268. this.registerPasswordType === 'password' ? 'text' : 'password'
  269. },
  270. toggleConfirmPasswordVisibility() {
  271. this.confirmPasswordType =
  272. this.confirmPasswordType === 'password' ? 'text' : 'password'
  273. },
  274. handleClose(done) {
  275. this.$refs.registerForm.resetFields()
  276. done()
  277. },
  278. handleRegister() {
  279. this.$refs.registerForm.validate((valid) => {
  280. if (valid) {
  281. this.registerLoading = true
  282. // 模拟注册请求
  283. post('register/', {
  284. name: this.registerForm.username,
  285. password1: this.registerForm.password,
  286. password2: this.registerForm.confirmPassword
  287. })
  288. .then((res) => {
  289. if (res.code === '200') {
  290. this.registerLoading = false
  291. this.$notify({
  292. title: '注册成功',
  293. message: '账号已成功创建,请使用新账号登录',
  294. type: 'success'
  295. })
  296. this.registerDialogVisible = false
  297. localStorage.setItem('openid', res.data.openid)
  298. // 自动填充注册信息到登录表单
  299. this.loginForm.username = this.registerForm.username
  300. this.loginForm.password = this.registerForm.password
  301. this.$nextTick(() => {
  302. this.$refs.password.focus()
  303. })
  304. } else {
  305. this.registerLoading = false
  306. this.$notify({
  307. title: '注册失败',
  308. message: res.msg,
  309. type: 'error'
  310. })
  311. }
  312. })
  313. .finally(() => {
  314. this.registerLoading = false
  315. })
  316. } else {
  317. this.registerLoading = false
  318. console.log('表单验证不通过')
  319. return false
  320. }
  321. })
  322. },
  323. // 原有方法保持不变
  324. checkCapslock(e) {
  325. const { key } = e
  326. this.capsTooltip = key && key.length === 1 && key >= 'A' && key <= 'Z'
  327. },
  328. showPwd() {
  329. if (this.passwordType === 'password') {
  330. this.passwordType = ''
  331. } else {
  332. this.passwordType = 'password'
  333. }
  334. this.$nextTick(() => {
  335. this.$refs.password.focus()
  336. })
  337. },
  338. handleLogin() {
  339. this.$refs.loginForm.validate((valid) => {
  340. if (valid) {
  341. this.loading = true
  342. this.$store
  343. .dispatch('user/login', this.loginForm)
  344. .then(() => {
  345. // 登录成功后获取用户信息
  346. return this.$store.dispatch('user/getInfo')
  347. })
  348. .then(() => {
  349. // 跳转到首页
  350. this.$router.push({
  351. path: this.redirect || '/',
  352. query: this.otherQuery
  353. })
  354. })
  355. .catch((error) => {
  356. // 显示错误通知
  357. this.$notify({
  358. title: '登录失败',
  359. message: error.message || '用户名或密码错误',
  360. type: 'error',
  361. duration: 5000
  362. })
  363. })
  364. .finally(() => {
  365. this.loading = false
  366. })
  367. } else {
  368. console.log('error submit!!')
  369. return false
  370. }
  371. })
  372. },
  373. getOtherQuery(query) {
  374. return Object.keys(query).reduce((acc, cur) => {
  375. if (cur !== 'redirect') {
  376. acc[cur] = query[cur]
  377. }
  378. return acc
  379. }, {})
  380. }
  381. }
  382. }
  383. </script>
  384. <style lang="scss">
  385. $bg: #283443;
  386. $light_gray: #fff;
  387. $cursor: #fff;
  388. @supports (-webkit-mask: none) and (not (cater-color: $cursor)) {
  389. .login-container .el-input input {
  390. color: $cursor;
  391. }
  392. }
  393. /* reset element-ui css */
  394. .login-container {
  395. .el-input {
  396. display: inline-block;
  397. height: 47px;
  398. width: 85%;
  399. input {
  400. background: transparent;
  401. border: 0px;
  402. -webkit-appearance: none;
  403. border-radius: 0px;
  404. padding: 12px 5px 12px 15px;
  405. color: $light_gray;
  406. height: 47px;
  407. caret-color: $cursor;
  408. &:-webkit-autofill {
  409. box-shadow: 0 0 0px 1000px $bg inset !important;
  410. -webkit-text-fill-color: $cursor !important;
  411. }
  412. }
  413. }
  414. .el-form-item {
  415. border: 1px solid rgba(255, 255, 255, 0.1);
  416. background: rgba(0, 0, 0, 0.1);
  417. border-radius: 5px;
  418. color: #454545;
  419. }
  420. }
  421. /* 注册弹窗样式 */
  422. .register-dialog {
  423. .el-dialog {
  424. border-radius: 8px;
  425. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.2);
  426. &__header {
  427. background-color: #409eff;
  428. padding: 15px 20px;
  429. border-top-left-radius: 8px;
  430. border-top-right-radius: 8px;
  431. .el-dialog__title {
  432. color: #fff;
  433. font-weight: bold;
  434. }
  435. .el-dialog__headerbtn {
  436. top: 15px;
  437. .el-dialog__close {
  438. color: #fff;
  439. }
  440. }
  441. }
  442. &__body {
  443. padding: 20px 25px;
  444. .el-form-item {
  445. margin-bottom: 20px;
  446. &:last-child {
  447. margin-bottom: 0;
  448. }
  449. .el-input__inner {
  450. height: 38px;
  451. line-height: 38px;
  452. color: #000; /* 设置输入框文字颜色为黑色 */
  453. }
  454. .el-form-item__error {
  455. padding-top: 4px;
  456. }
  457. }
  458. }
  459. &__footer {
  460. padding: 10px 20px 20px;
  461. text-align: center;
  462. .el-button {
  463. width: 100px;
  464. margin: 0 10px;
  465. }
  466. }
  467. }
  468. }
  469. </style>
  470. <style lang="scss" scoped>
  471. $bg: #2d3a4b;
  472. $dark_gray: #889aa4;
  473. $light_gray: #eee;
  474. .login-container {
  475. min-height: 100%;
  476. width: 100%;
  477. background-color: $bg;
  478. overflow: hidden;
  479. .login-form {
  480. position: relative;
  481. width: 520px;
  482. max-width: 100%;
  483. padding: 160px 35px 0;
  484. margin: 0 auto;
  485. overflow: hidden;
  486. }
  487. .tips {
  488. font-size: 14px;
  489. color: #fff;
  490. margin-bottom: 10px;
  491. span {
  492. &:first-of-type {
  493. margin-right: 16px;
  494. }
  495. }
  496. }
  497. .svg-container {
  498. padding: 6px 5px 6px 15px;
  499. color: $dark_gray;
  500. vertical-align: middle;
  501. width: 30px;
  502. display: inline-block;
  503. }
  504. .title-container {
  505. position: relative;
  506. .title {
  507. font-size: 26px;
  508. color: $light_gray;
  509. margin: 0px auto 40px auto;
  510. text-align: center;
  511. font-weight: bold;
  512. }
  513. }
  514. .show-pwd {
  515. position: absolute;
  516. right: 10px;
  517. top: 7px;
  518. font-size: 16px;
  519. color: $dark_gray;
  520. cursor: pointer;
  521. user-select: none;
  522. }
  523. .thirdparty-button {
  524. position: absolute;
  525. right: 0;
  526. bottom: 6px;
  527. }
  528. @media only screen and (max-width: 470px) {
  529. .thirdparty-button {
  530. display: none;
  531. }
  532. }
  533. }
  534. </style>