tel-verify.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. <template>
  2. <view class="tel-verify" :class="showVerify && delayHide ? 'bg-in' : 'bg-out'">
  3. <custom-toast ref='toast'></custom-toast>
  4. <view class="tel-verify-dialog" :class="showVerify && delayHide ? 'dialog-in' : 'dialog-out'">
  5. <view class="top">
  6. <text>手机号验证</text>
  7. </view>
  8. <open-data class="image" type="userAvatarUrl"></open-data>
  9. <text class="close cuIcon-close" @tap="close"></text>
  10. <view class="mid">
  11. <view class="mid-top mid-item">
  12. <input type="text" class="tel-input" v-model="tel" maxlength="11">
  13. <text class="placeholder" v-if="!tel">请输入手机号</text>
  14. <text v-if="tel" @tap="clearTel" class="clear-tel cuIcon-roundclosefill"></text>
  15. </view>
  16. <view v-if="telWrong" :class="shake ? shake : ''" class="mid-mid">
  17. <text class="cuIcon-info"></text>
  18. <text>手机号填写错误</text>
  19. </view>
  20. <view class="mid-bot mid-item">
  21. <input type="text" class="yzm-input" v-model="verify" maxlength="6">
  22. <text class="placeholder" v-if="!verify">请输入6位验证码</text>
  23. <text v-if="verify" @tap="clearYzm" class="clear-yzm cuIcon-roundclosefill"></text>
  24. <text class="getYZM" @tap="getYZM">获取验证码<text v-if="countDown">{{ ' ' + countDown }}</text></text>
  25. </view>
  26. <view class="mid-bot-text">若认证失败请联系上级进行信息确认</view>
  27. </view>
  28. <button hover-class="hover" class="bot" @tap="submit">提交</button>
  29. </view>
  30. </view>
  31. </template>
  32. <script>
  33. import { api_getYZM, api_submitTelYZM, api_changeTel } from '../api.js'
  34. export default {
  35. props: {
  36. changeTel: Boolean
  37. },
  38. data() {
  39. return {
  40. delayHide: true, //开始 淡出 动画
  41. tel: '', //手机号
  42. verify: '', //验证码
  43. verify_key: '', //验证码 key
  44. telWrong: false, //手机号错误文字提醒
  45. shake: '', //手机号错误文字提醒动画类名
  46. countDown: 0, //验证码已发送倒计时
  47. requesting: false,
  48. submiting: false
  49. }
  50. },
  51. computed: {
  52. showVerify () { //手机验证弹框显示状态
  53. return this.$store.state.showVerify
  54. },
  55. userWeixinInfo () { //用户微信信息
  56. return this.$store.state.userWeixinInfo
  57. }
  58. },
  59. watch: {
  60. verify (n) { //监听验证码输入,输入结束后且通过验证,收起手机软键盘
  61. if (n.match(/^\d{6}$/) && this.tel.match(/^1\d{10}$/)) {
  62. uni.hideKeyboard()
  63. }
  64. }
  65. },
  66. methods: {
  67. clearTel () { //清除手机号
  68. this.tel = ''
  69. },
  70. clearYzm () { //清除验证码
  71. this.verify = ''
  72. },
  73. getYZM () { //点击发送验证码
  74. if (this.tel.match(/^1\d{10}$/)) { //手机号校验
  75. this.telWrong = false //如果出现了红色提示文字,隐藏文字
  76. if (this.countDown) { //如果正在倒计时,表示验证码已发送
  77. this.$refs.toast.hover('验证码已发送,请稍后重试', 2345, 'bottom')
  78. } else { //发送网络请求
  79. if (!this.requesting) {
  80. this.requesting = true
  81. uni.showLoading({ title: '加载中', mask: true })
  82. this.$ajax.get(`${api_getYZM}?phone=${this.tel}`).then(([ , { data: res }]) => {
  83. this.requesting = false
  84. this.$hideLoading()
  85. if (res.code === 200) { //验证码发送成功,开始倒计时
  86. this.verify_key = res.data.verify_key
  87. this.$refs.toast.hover('验证码发送成功', 2345, 'bottom')
  88. this.countDown = 90
  89. this.timer = setInterval(() => {
  90. this.countDown --
  91. if (!this.countDown) {
  92. this.countDown = 0
  93. clearInterval(this.timer)
  94. }
  95. }, 1111)
  96. } else if (res.code === 400) { //手机号不存在
  97. uni.showModal({
  98. title: '手机号不存在',
  99. content: '请确认手机号或联系上级代理进行信息确认',
  100. showCancel: false,
  101. confirmText: '确定',
  102. success: res => {
  103. if (res.confirm) {
  104. this.tel = ''
  105. }
  106. }
  107. })
  108. } else if (res.code === 600) { //手机号已注册
  109. uni.showModal({
  110. title: '手机号已注册',
  111. content: '请确认手机号是否输入正确',
  112. showCancel: false,
  113. confirmText: '确定',
  114. success: res => {
  115. if (res.confirm) {
  116. this.tel = ''
  117. }
  118. }
  119. })
  120. } else {
  121. uni.showModal({
  122. title: '提示',
  123. content: res.message,
  124. showCancel: false
  125. })
  126. }
  127. })
  128. }
  129. }
  130. } else { //手机号校验不通过,出现红色抖动文字提示用户
  131. this.telWrong = true
  132. this.shake = 'animation-shake'
  133. setTimeout(e => {
  134. this.shake = ''
  135. }, 345)
  136. }
  137. },
  138. async submit () { //点击提交
  139. if (this.tel.match(/^1\d{10}$/) && this.verify.match(/^\d{6}$/)) { //是校验输入是否合法
  140. if (!this.submiting) {
  141. this.submiting = true
  142. uni.showLoading({ title: '', mask: true }) //显示loading
  143. const [ , { code }] = await uni.login() //获取 code
  144. this.$ajax.post(this.changeTel ? api_changeTel : api_submitTelYZM , { //提交手机号 验证码 用户头像
  145. phone: this.tel,
  146. verify_code: this.verify,
  147. verify_key: this.verify_key,
  148. avatar: this.userWeixinInfo.avatarUrl,
  149. code
  150. }).then(([ , { data: res }]) => {
  151. setTimeout(() => {
  152. this.$hideLoading()
  153. this.submiting = false
  154. }, 345)
  155. if (res.code === 200) {
  156. uni.$emit('MESSAGE', '验证成功')
  157. this.$store.dispatch('onLaunch') //触发初始化方法
  158. this.close()
  159. } else if (res.code === 300) {
  160. this.verify = ''
  161. this.$refs.toast.hover('验证码错误,请重新获取', 2345, 'bottom')
  162. } else if (res.code === 400) {
  163. this.verify = ''
  164. this.$refs.toast.hover('验证码已超时,请重新输入', 2345, 'bottom')
  165. } else if (res.code === 600) { // 表示用户已经绑定过手机号
  166. this.close()
  167. this.$store.dispatch('onLaunch') //触发初始化方法
  168. } else {
  169. this.tel = ''
  170. this.verify = ''
  171. this.$refs.toast.hover('验证码或手机号无效,请重新输入', 2345, 'bottom')
  172. }
  173. })
  174. }
  175. } else {
  176. this.$refs.toast.hover('请输入正确的手机号和验证码', 2345, 'bottom')
  177. }
  178. },
  179. close () { //关闭手机验证弹窗
  180. this.delayHide = false
  181. setTimeout(e => {
  182. this.$store.commit('HIDEVERIFY')
  183. this.delayHide = true
  184. }, 100)
  185. }
  186. }
  187. }
  188. </script>
  189. <style lang="scss" scoped>
  190. .tel-verify {
  191. position: fixed;
  192. left: 0;
  193. right: 0;
  194. top: 0;
  195. bottom: 0;
  196. background: rgba(0, 0, 0, .3);
  197. z-index: 2;
  198. .tel-verify-dialog {
  199. position: fixed;
  200. width: 576rpx;
  201. height: 706rpx;
  202. left: 50%;
  203. top: 50%;
  204. margin-left: -288rpx;
  205. margin-top: -353rpx;
  206. background: #FFFFFF;
  207. border-radius: 20rpx;
  208. overflow: hidden;
  209. .image {
  210. width: 138rpx;
  211. height: 138rpx;
  212. position: absolute;
  213. left: 50%;
  214. top: 150rpx;
  215. transform: translateX(-50%);
  216. border-radius: 50%;
  217. border: 6rpx solid #FFFFFF;
  218. overflow: hidden;
  219. }
  220. .close {
  221. position: absolute;
  222. top: 30rpx;
  223. right: 30rpx;
  224. font-size: 50rpx;
  225. color: rgba(255, 255, 255, .7);
  226. }
  227. .top {
  228. height: 220rpx;
  229. background:rgba(250, 99, 66, 1);
  230. display: flex;
  231. flex-direction: column;
  232. align-items: center;
  233. text {
  234. font-size: 40rpx;
  235. font-family: "PingFang SC";
  236. font-weight: 400;
  237. color: rgba(255,255,255,1);
  238. line-height: 46rpx;
  239. margin-top: 50rpx;
  240. }
  241. }
  242. .mid {
  243. height: 390rpx;
  244. position: relative;
  245. .mid-item {
  246. position: absolute;
  247. width: 450rpx;
  248. height: 70rpx;
  249. left: 60rpx;
  250. display: flex;
  251. input {
  252. height: 100%;
  253. border: none;
  254. box-sizing: border-box;
  255. padding: 0 16rpx;
  256. font-size: 28rpx;
  257. border-radius: 2rpx;
  258. background: rgba(238,238,238,1);
  259. &.tel-input {
  260. width: 100%;
  261. }
  262. &.yzm-input {
  263. width: 240rpx;
  264. }
  265. }
  266. .cuIcon-roundclosefill {
  267. position: absolute;
  268. z-index: 2;
  269. @include text(40rpx);
  270. line-height: 70rpx;
  271. color: rgba(178,178,178,1);
  272. &.clear-tel {
  273. right: 8rpx;
  274. }
  275. &.clear-yzm {
  276. left: 182rpx;
  277. }
  278. }
  279. .placeholder {
  280. position: absolute;
  281. @include text(28rpx);
  282. width: auto;
  283. top: 22rpx;
  284. left: 17rpx;
  285. color: rgba(178,178,178,1);
  286. }
  287. .getYZM {
  288. height: 100%;
  289. flex: 1;
  290. font-size: 30rpx;
  291. color: rgba(250,99,66,1);
  292. display: flex;
  293. justify-content: center;
  294. align-items: center;
  295. }
  296. }
  297. .mid-top {
  298. top: 100rpx;
  299. }
  300. .mid-mid {
  301. position: absolute;
  302. height: 30rpx;
  303. line-height: 30rpx;
  304. font-size: 20rpx;
  305. top: 178rpx;
  306. left: 60rpx;
  307. font-weight:bold;
  308. color: rgba(250,99,66,1);
  309. }
  310. .mid-bot {
  311. top: 216rpx;
  312. }
  313. .mid-bot-text {
  314. position: absolute;
  315. top: 314rpx;
  316. width: 100%;
  317. @include text(24rpx);
  318. text-align: center;
  319. color: rgba(153,153,153,1);
  320. }
  321. }
  322. .bot {
  323. height: 96rpx;
  324. line-height: 96rpx;
  325. text-align: center;
  326. font-size: 32rpx;
  327. font-family: "Source Han Sans CN";
  328. font-weight: 400;
  329. color: rgba(250,99,66,1);
  330. background:rgba(254, 225, 218, 1);
  331. border-radius: 0;
  332. &.hover {
  333. background:rgba(250, 99, 66, 1);
  334. color: #FFFFFF;
  335. }
  336. }
  337. }
  338. }
  339. @keyframes dialog-in {
  340. 0% {
  341. transform: scale(0, 0);
  342. }
  343. 100% {
  344. transform: scale(1, 1);
  345. }
  346. }
  347. @keyframes dialog-out {
  348. 0% {
  349. transform: scale(1, 1);
  350. }
  351. 100% {
  352. transform: scale(0, 0);
  353. }
  354. }
  355. @keyframes bg-in {
  356. 0% {
  357. background: rgba(0, 0, 0, 0)
  358. }
  359. 100% {
  360. background: rgba(0, 0, 0, .4)
  361. }
  362. }
  363. @keyframes bg-out {
  364. 0% {
  365. background: rgba(0, 0, 0, .4)
  366. }
  367. 100% {
  368. background: rgba(0, 0, 0, 0)
  369. }
  370. }
  371. .bg-in {
  372. animation: bg-in .2s;
  373. animation-fill-mode: forwards;
  374. }
  375. .bg-out {
  376. animation: bg-out .1s;
  377. animation-fill-mode: forwards;
  378. }
  379. .dialog-in {
  380. animation: dialog-in .2s;
  381. animation-fill-mode: forwards;
  382. }
  383. .dialog-out {
  384. animation: dialog-out .1s;
  385. animation-fill-mode: forwards;
  386. }
  387. </style>