index.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. <template>
  2. <div class="one-click-bind">
  3. <div class="bind-header">
  4. <span
  5. >根据《中华人民共和国网络安全法》要求,<br />
  6. 为避免非正常使用标讯信息,需完成手机号绑定</span
  7. >
  8. </div>
  9. <div class="bind-form">
  10. <van-field
  11. v-model.trim="info.phone"
  12. label="手机号"
  13. type="tel"
  14. maxlength="11"
  15. placeholder="请输入手机号码"
  16. :error-message="errorMessage.phone"
  17. @blur="checkPhoneRegPass"
  18. ></van-field>
  19. <van-field
  20. v-show="picCode.show"
  21. v-model.trim="info.picCode"
  22. label=""
  23. maxlength="6"
  24. placeholder="图形验证码"
  25. :error-message="errorMessage.picCode"
  26. >
  27. <template #button>
  28. <div class="pic-code" @click="refreshCaptcha">
  29. <img :src="imgBase64Complete" v-if="picCode.imgBase64" />
  30. <van-loading size="24" v-else></van-loading>
  31. </div>
  32. </template>
  33. </van-field>
  34. <van-field
  35. v-model.trim="info.code"
  36. label="验证码"
  37. maxlength="6"
  38. placeholder="请输入验证码"
  39. :error-message="errorMessage.code"
  40. >
  41. <template #button>
  42. <van-button
  43. class="send-code"
  44. :class="{ active: info.phone }"
  45. size="small"
  46. :disabled="sendCodeButtonDisabled"
  47. @click="sendVerifyCode"
  48. >{{ sendCodeButtonText }}</van-button
  49. >
  50. </template>
  51. </van-field>
  52. </div>
  53. <div class="bind-footer">
  54. <div class="j-button-group">
  55. <button
  56. class="j-button-confirm"
  57. @click="onSubmit"
  58. :disabled="confirmButtonDisabled"
  59. >
  60. 立即绑定
  61. </button>
  62. </div>
  63. <div class="fast-login-container">
  64. <button
  65. class="fast-login-text fast-login-open-link highlight-text"
  66. @click="getPhoneBind"
  67. >
  68. 本机号码一键绑定
  69. </button>
  70. </div>
  71. <div class="tip-text" v-if="showBottomTip">
  72. 根据《中华人民共和国网络安全法》要求,为避免非正常使用标讯信息,需完成手机号绑定,
  73. 保障账户安全和信息的正当使用。
  74. </div>
  75. </div>
  76. </div>
  77. </template>
  78. <script>
  79. import { Field, Button, Loading } from 'vant'
  80. import { getPhoneCaptcha, setPhoneBind } from '@/api/modules/'
  81. import fastBind from '@/utils/mixins/modules/fast-bind'
  82. export default {
  83. name: 'OneClickBinding',
  84. mixins: [fastBind],
  85. components: {
  86. [Field.name]: Field,
  87. [Button.name]: Button,
  88. [Loading.name]: Loading
  89. },
  90. data() {
  91. return {
  92. info: {
  93. phone: '',
  94. picCode: '',
  95. code: ''
  96. },
  97. errorMessage: {
  98. phone: '',
  99. picCode: '',
  100. code: ''
  101. },
  102. sendCodeButton: {
  103. timerId: 0,
  104. timeStartDefault: 60,
  105. defaultValue: '发送验证码',
  106. count: 0
  107. },
  108. picCode: {
  109. show: false,
  110. imgBase64: '',
  111. cacheShow: false,
  112. cacheImgBase64: ''
  113. },
  114. conf: {
  115. phoneReg: /^1[3-9]\d{9}$/
  116. },
  117. showBottomTip: false
  118. }
  119. },
  120. computed: {
  121. confirmButtonDisabled() {
  122. let hasEmpty = false
  123. if (this.picCode.show) {
  124. hasEmpty = !this.info.phone || !this.info.code || !this.info.picCode
  125. } else {
  126. hasEmpty = !this.info.phone || !this.info.code
  127. }
  128. const pass = this.conf.phoneReg.test(this.info.phone)
  129. return hasEmpty || !pass
  130. },
  131. sendCodeButtonText() {
  132. const dText = this.sendCodeButton.defaultValue
  133. return this.sendCodeButton.count <= 0
  134. ? dText
  135. : `重新发送(${this.sendCodeButton.count}s)`
  136. },
  137. sendCodeButtonDisabled() {
  138. return this.sendCodeButton.count > 0
  139. },
  140. imgBase64Complete() {
  141. return 'data:image/png;base64,' + this.picCode.imgBase64
  142. }
  143. },
  144. methods: {
  145. showToast(message) {
  146. this.$toast({
  147. duration: 1500,
  148. forbidClick: true,
  149. message: message
  150. })
  151. },
  152. showLoading() {
  153. return this.$toast.loading({
  154. duration: 0,
  155. forbidClick: true,
  156. message: 'loading...'
  157. })
  158. },
  159. async getImgCaptcha(needCache) {
  160. if (this.picCode.cacheImgBase64) {
  161. this.picCode.show = this.picCode.cacheShow
  162. this.picCode.imgBase64 = this.picCode.cacheImgBase64
  163. this.picCode.cacheImgBase64 = ''
  164. return
  165. }
  166. const { error_code: code, error_msg: msg, data } = await getPhoneCaptcha()
  167. if (code === 0 && data) {
  168. // 是否缓存图片
  169. if (needCache == 'cache') {
  170. // 将下一张图片的状态缓存
  171. this.picCode.cacheShow = data.needVerify
  172. this.picCode.cacheImgBase64 = data.imageData
  173. } else {
  174. this.picCode.show = data.needVerify
  175. this.picCode.imgBase64 = data.imageData
  176. }
  177. } else {
  178. this.showToast(msg)
  179. }
  180. },
  181. refreshCaptcha() {
  182. this.picCode.imgBase64 = ''
  183. this.getImgCaptcha()
  184. },
  185. checkPhoneRegPass() {
  186. let pass = this.conf.phoneReg.test(this.info.phone)
  187. if (this.info.phone) {
  188. if (pass) {
  189. this.errorMessage.phone = ''
  190. } else {
  191. this.errorMessage.phone = '手机号格式不正确'
  192. }
  193. } else {
  194. this.errorMessage.phone = ''
  195. }
  196. return pass
  197. },
  198. startSendCodeTimer(t) {
  199. this.sendCodeButton.count = t || this.sendCodeButton.timeStartDefault
  200. this.sendCodeButton.timerId = setInterval(() => {
  201. this.sendCodeButton.count--
  202. if (this.sendCodeButton.count <= 0) {
  203. // 倒计时结束
  204. clearInterval(this.sendCodeButton.timerId)
  205. // 倒计时结束,刷新验证码
  206. if (this.picCode.cacheImgBase64) {
  207. this.picCode.show = this.picCode.cacheShow
  208. this.picCode.imgBase64 = this.picCode.cacheImgBase64
  209. this.picCode.cacheImgBase64 = ''
  210. } else {
  211. this.refreshCaptcha()
  212. }
  213. }
  214. }, 1000)
  215. },
  216. // 发送验证码
  217. async sendVerifyCode() {
  218. const pass = this.checkPhoneRegPass()
  219. if (!pass) return
  220. const loading = this.showLoading()
  221. const params = {
  222. phone: this.info.phone,
  223. code: this.info.picCode,
  224. step: 1
  225. }
  226. const {
  227. error_code: code,
  228. error_msg: msg,
  229. data
  230. } = await setPhoneBind(params, 'bind')
  231. loading.clear()
  232. if (code === 0 && data?.state === 1) {
  233. this.startSendCodeTimer()
  234. this.showToast('验证码发送成功')
  235. this.getImgCaptcha('cache')
  236. } else {
  237. this.showToast(msg || '验证码发送失败')
  238. this.refreshCaptcha()
  239. }
  240. },
  241. bindPhoneSuccess(state) {
  242. console.log(state, '组件内接收参数')
  243. if (state === 1) {
  244. let newQuery = JSON.parse(JSON.stringify(this.$route.query))
  245. if (newQuery && newQuery.wxpush) {
  246. delete newQuery.wxpush
  247. }
  248. this.$router.replace({ query: newQuery })
  249. }
  250. },
  251. async onSubmit() {
  252. const pass = this.checkPhoneRegPass()
  253. if (!pass) return
  254. const loading = this.showLoading()
  255. const params = {
  256. phone: this.info.phone,
  257. code: this.info.code,
  258. step: 2
  259. }
  260. const {
  261. error_code: code,
  262. error_msg: msg,
  263. data
  264. } = await setPhoneBind(params, 'bind')
  265. loading.clear()
  266. if (code === 0 && data) {
  267. this.bindPhoneSuccess(data.state)
  268. } else {
  269. this.$toast(msg)
  270. }
  271. }
  272. }
  273. }
  274. </script>
  275. <style lang="scss">
  276. @import '@/assets/style/modules/fast-login.scss';
  277. </style>
  278. <style lang="scss" scoped>
  279. .one-click-bind {
  280. margin: 12px;
  281. border: 2px solid $color_main;
  282. border-radius: 12px;
  283. background: #fff;
  284. .bind-header {
  285. padding: 16px 8px 12px;
  286. font-size: 14px;
  287. line-height: 20px;
  288. color: #171826;
  289. text-align: center;
  290. }
  291. .bind-form {
  292. padding: 0 8px;
  293. .send-code {
  294. padding: 0;
  295. height: unset;
  296. color: #c0c4cc;
  297. font-size: 14px;
  298. line-height: 20px;
  299. border: none;
  300. &.active {
  301. color: $color_main;
  302. }
  303. }
  304. ::v-deep {
  305. .van-cell {
  306. align-items: center;
  307. padding: 16px;
  308. }
  309. .van-field__label {
  310. width: 75px;
  311. margin-right: 8px;
  312. font-size: 15px;
  313. line-height: 22px;
  314. color: #5f5e64;
  315. }
  316. .van-field__control {
  317. font-size: 16px;
  318. }
  319. .van-field__button {
  320. display: flex;
  321. }
  322. }
  323. }
  324. .fast-login-container {
  325. text-align: center;
  326. }
  327. .fast-login-text {
  328. padding-bottom: 20px;
  329. font-size: 16px;
  330. line-height: 26px;
  331. background-color: transparent;
  332. }
  333. }
  334. </style>