tel-verify.vue 10.0 KB

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