index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <template>
  2. <div class="simple-login">
  3. <van-overlay :show="show" @click="show = false">
  4. <div class="wrapper flex justify-center items-center" @click.stop>
  5. <div class="block flex items-center flex-col py-6">
  6. <div class="close" @click="onClose"></div>
  7. <div class="title pb-6">登录</div>
  8. <div class="form">
  9. <div class="form-item mb-4">
  10. <input
  11. type="text"
  12. placeholder="手机号"
  13. v-model="formData.mobile"
  14. maxlength="11"
  15. />
  16. </div>
  17. <div class="form-item mb-4 code" v-if="formType !== 'login'">
  18. <input
  19. type="text"
  20. placeholder="验证码"
  21. class="code"
  22. v-model="formData.verifyCode"
  23. maxlength="6"
  24. />
  25. <span class="send" @click="onSend">{{ sendCodeBtnText }}</span>
  26. </div>
  27. <div class="form-item mb-4">
  28. <input
  29. type="text"
  30. placeholder="密码"
  31. v-model="formData.password"
  32. maxlength="11"
  33. />
  34. </div>
  35. <div class="form-item mb-4" v-if="formType === 'register'">
  36. <input
  37. type="text"
  38. placeholder="确认密码"
  39. v-model="formData.confirmPwd"
  40. />
  41. </div>
  42. <div class="submit" @click="onSubmit">{{ submitText }}</div>
  43. <div
  44. class="flex justify-between control mt-2"
  45. v-if="formType === 'login'"
  46. >
  47. <span class="forget">忘记密码</span>
  48. <span class="regist">立即注册</span>
  49. </div>
  50. </div>
  51. </div>
  52. </div>
  53. </van-overlay>
  54. </div>
  55. </template>
  56. <script>
  57. import { mapGetters } from 'vuex'
  58. import { isMobile } from '@/utils/validator'
  59. export default {
  60. name: 'simple-login',
  61. props: {
  62. type: {
  63. type: String,
  64. default: 'login',
  65. },
  66. },
  67. data() {
  68. return {
  69. formType: 'login',
  70. show: false,
  71. sendStatus: 0,
  72. formData: {
  73. authUserId: '',
  74. mobile: '',
  75. verifyCode: '',
  76. password: '',
  77. confirmPwd: '',
  78. },
  79. timer: null,
  80. }
  81. },
  82. computed: {
  83. ...mapGetters(['loginVisiable', 'authUserId', 'routePrefix']),
  84. sendCodeBtnText() {
  85. return this.sendStatus === 0
  86. ? '发送验证码'
  87. : `再次发送${this.sendStatus}s`
  88. },
  89. submitText() {
  90. return this.formType === 'login'
  91. ? '登录'
  92. : this.formType === 'forget'
  93. ? '确定'
  94. : '提交'
  95. },
  96. },
  97. created() {
  98. this.formType = this.type
  99. },
  100. watch: {
  101. loginVisiable() {
  102. this.show = this.loginVisiable
  103. },
  104. type: {
  105. handler: function (nval) {
  106. this.formType = nval
  107. console.log(nval)
  108. },
  109. immediate: true,
  110. },
  111. },
  112. methods: {
  113. async onSubmit() {
  114. // 验证手机号是否合法
  115. if (!isMobile(this.formData.mobile)) {
  116. this.$toast('请输入正确的手机号')
  117. return
  118. }
  119. if (!this.formData.verifyCode) {
  120. this.$toast('请输入验证码')
  121. return
  122. }
  123. try {
  124. this.formData.authUserId = this.authUserId
  125. const res = await this.$http.api.customLogin(this.formData)
  126. this.$store.dispatch('user/login', res.data)
  127. this.$setStorage(this.routePrefix, 'userInfo', res.data)
  128. // 关闭登录窗口
  129. this.onClose()
  130. // 重定向
  131. const login_redicret = this.$getStorage(
  132. this.routePrefix,
  133. 'login_redicret'
  134. )
  135. if (login_redicret && login_redicret !== this.$route.path) {
  136. this.$router.push(login_redicret)
  137. }
  138. } catch (error) {
  139. console.log(error)
  140. }
  141. },
  142. // 关闭登录窗口
  143. onClose() {
  144. this.$store.commit('app/HIDE_LOGIN')
  145. this.formData.mobile = ''
  146. this.formData.verifyCode = ''
  147. this.formData.authUserId = ''
  148. },
  149. async onSend() {
  150. if (this.sendStatus > 0) return
  151. // 验证手机号是否合法
  152. if (!isMobile(this.formData.mobile)) {
  153. this.$toast('请输入正确的手机号')
  154. return
  155. }
  156. try {
  157. // 发送验证码
  158. const res = await this.$http.api.sendVerifyCode({
  159. mobile: this.formData.mobile,
  160. authUserId: this.authUserId,
  161. type: 1,
  162. })
  163. this.$toast('验证码已发送')
  164. // 开启倒计时
  165. this.countdown()
  166. } catch (error) {
  167. console.log(error)
  168. }
  169. },
  170. countdown() {
  171. this.sendStatus = 30
  172. this.timer = setInterval(() => {
  173. if (this.sendStatus === 0) {
  174. clearInterval(this.timer)
  175. return
  176. }
  177. this.sendStatus--
  178. }, 1000)
  179. },
  180. },
  181. }
  182. </script>
  183. <style scoped lang="scss">
  184. @media screen and (min-width: 768px) {
  185. .wrapper {
  186. height: 100vh;
  187. .block {
  188. position: relative;
  189. width: 400px;
  190. background: #fff;
  191. .close {
  192. position: absolute;
  193. right: 16px;
  194. top: 16px;
  195. width: 24px;
  196. height: 24px;
  197. background: url(https://static.caimei365.com/www/authentic/h5/icon-close.png)
  198. center no-repeat;
  199. background-size: 24px 24px;
  200. cursor: pointer;
  201. }
  202. .title {
  203. font-size: 24px;
  204. color: #101010;
  205. }
  206. .form-item {
  207. position: relative;
  208. width: 326px;
  209. input {
  210. width: 326px;
  211. display: block;
  212. padding: 14px 16px;
  213. font-size: 14px;
  214. border: 1px solid #d8d8d8;
  215. box-sizing: border-box;
  216. &.code {
  217. width: 225px;
  218. }
  219. }
  220. .send {
  221. position: absolute;
  222. right: 0;
  223. top: 50%;
  224. transform: translateY(-50%);
  225. font-size: 16px;
  226. cursor: pointer;
  227. @include themify($themes) {
  228. color: themed('color');
  229. }
  230. }
  231. }
  232. .control {
  233. font-size: 14px;
  234. .forget,
  235. .regist {
  236. cursor: pointer;
  237. }
  238. .forget {
  239. color: #666;
  240. &:hover {
  241. @include themify($themes) {
  242. color: themed('color');
  243. }
  244. }
  245. }
  246. .regist {
  247. @include themify($themes) {
  248. color: themed('color');
  249. }
  250. }
  251. }
  252. .submit {
  253. width: 326px;
  254. height: 46px;
  255. font-size: 16px;
  256. color: #fff;
  257. text-align: center;
  258. line-height: 46px;
  259. transition: all 0.4s;
  260. cursor: pointer;
  261. @include themify($themes) {
  262. background: themed('color');
  263. }
  264. &:hover {
  265. @include themify($themes) {
  266. background: themed('hover-color');
  267. }
  268. }
  269. }
  270. }
  271. }
  272. }
  273. @media screen and (max-width: 768px) {
  274. .wrapper {
  275. height: 100vh;
  276. .block {
  277. position: relative;
  278. width: 76vw;
  279. background: #fff;
  280. .close {
  281. position: absolute;
  282. right: 2.4vw;
  283. top: 2.4vw;
  284. width: 5.6vw;
  285. height: 5.6vw;
  286. background: url(https://static.caimei365.com/www/authentic/h5/icon-close.png)
  287. center no-repeat;
  288. background-size: 4.8vw 4.8vw;
  289. cursor: pointer;
  290. }
  291. .title {
  292. font-size: 4.8vw;
  293. color: #101010;
  294. }
  295. .form-item {
  296. position: relative;
  297. width: 62vw;
  298. input {
  299. width: 62vw;
  300. display: block;
  301. padding: 1.8vw 2.4vw;
  302. font-size: 14px;
  303. border: 0.1vw solid #d8d8d8;
  304. box-sizing: border-box;
  305. &.code {
  306. width: 42.8vw;
  307. }
  308. }
  309. .send {
  310. position: absolute;
  311. right: 0;
  312. top: 50%;
  313. transform: translateY(-50%);
  314. font-size: 3.2vw;
  315. cursor: pointer;
  316. @include themify($themes) {
  317. color: themed('color');
  318. }
  319. }
  320. }
  321. .control {
  322. font-size: 3.2vw;
  323. .forget,
  324. .regist {
  325. cursor: pointer;
  326. }
  327. .forget {
  328. color: #666;
  329. &:hover {
  330. @include themify($themes) {
  331. color: themed('color');
  332. }
  333. }
  334. }
  335. .regist {
  336. @include themify($themes) {
  337. color: themed('color');
  338. }
  339. }
  340. }
  341. .submit {
  342. width: 62vw;
  343. height: 8.8vw;
  344. font-size: 3.2vw;
  345. color: #fff;
  346. text-align: center;
  347. line-height: 8.8vw;
  348. transition: all 0.4s;
  349. @include themify($themes) {
  350. background: themed('color');
  351. }
  352. }
  353. }
  354. }
  355. }
  356. </style>