goLogin.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <template>
  2. <uni-popup ref="dlRef" :animation="true" :is-mask-click="false" mask-background-color="rgba(255, 255, 255, 0.6);">
  3. <view class="ezy-login-page go-login-page">
  4. <view @click="handleBack" class="ezy-nav-bar-icon yzm-nav-bar-icon"></view>
  5. <icon class="login-img"></icon>
  6. <view class="login-body-box">
  7. <!-- 用户名 -->
  8. <view class="phone-input-box">
  9. <view class="phone-prefix">+86</view>
  10. <input class="phone-input" type="text" v-model="loginData.phoneNumber" placeholder="请输入手机号" maxlength="11"
  11. @input="clearTelInput" />
  12. <view class="close-btn" v-if="loginData.clearTelIcon" @click="clearTel"></view>
  13. </view>
  14. <!-- 验证码 -->
  15. <view class="yzm-row">
  16. <view class="phone-input-box">
  17. <input class="phone-input" type="text" v-model="loginData.yzmNumber" placeholder="请输入验证码" maxlength="4"
  18. @input="clearYzmInput" />
  19. <view class="close-btn" v-if="loginData.clearYzmIcon" @click="clearYzm"></view>
  20. </view>
  21. <text class="yzm-text-btn" @click="startCountdown">{{loginData.buttonText}}</text>
  22. <!-- <text class="cxfs-btn" @click="startCountdown"
  23. :class="{ 'cxfs-btn-disabled': loginData.isDisabled}">{{loginData.buttonText}}</text> -->
  24. </view>
  25. <!-- 隐私协议 -->
  26. <view class="agreement-checkbox-box">
  27. <checkbox-group @change="handleUpdateAgree">
  28. <checkbox class="agreement-checkbox-input" color="#FFFFFF" value="agree" :checked="isAgreed" />
  29. </checkbox-group>
  30. <view class="agreement-text-box">
  31. 我已阅读并同意
  32. <view class="agreement-text" @click="agreeBtn('yhxy')">《用户协议》</view>
  33. <view @click="agreeBtn('ystk')" class="agreement-text">《隐私政策》</view>
  34. </view>
  35. </view>
  36. <!-- 登录按钮 -->
  37. <ezyActiveVue class="ezy-btn-active login-btn yzm-btn" @aclick="handleLogin" :class="loginData.yzmStatus">登录
  38. </ezyActiveVue>
  39. </view>
  40. </view>
  41. </uni-popup>
  42. <!-- 政策协议 -->
  43. <agree-content-dialog ref="agreeContentDialogRef" :agreeType="agreeType"></agree-content-dialog>
  44. <!-- 图形验证码 -->
  45. <captchaVue ref="captcha" :config="config" @captchaSuccess="captchaSuccess" @captchaError="captchaError"
  46. @captchaFail="captchaFail" @captchaReady="captchaReady" @captchaClose="captchaClose"></captchaVue>
  47. <!-- 确认协议 -->
  48. <agree-dialog ref="agreeDialogRef" @confirm-btn="confirmBtn"></agree-dialog>
  49. </template>
  50. <script setup>
  51. import {
  52. ref,
  53. reactive
  54. } from "vue"
  55. import {
  56. toast
  57. } from "@/utils/common";
  58. import {
  59. login,
  60. telBind,
  61. sendCode
  62. } from "@/api/login";
  63. import cacheManager from "@/utils/cacheManager";
  64. import agreeContentDialog from '@/pages/login/agreeContentDialog.vue';
  65. import agreeDialog from '@/pages/login/agreeDialog.vue'
  66. import captchaVue from "@/components/captcha4/index.vue";
  67. import ezyActiveVue from "@/components/ezyActive/ezyActive.vue"
  68. const emits = defineEmits(['success'])
  69. const dlRef = ref(null)
  70. const captcha = ref(null);
  71. const agreeType = ref(null);
  72. const agreeDialogRef = ref(null);
  73. const agreeContentDialogRef = ref(null);
  74. const isAgreed = ref(false);
  75. const config = ref({
  76. captchaId: "9d5837b0807b8de44da0de310a0b2813",
  77. });
  78. const loginData = reactive({
  79. phoneNumber: null,
  80. yzmNumber: null,
  81. clearYzmIcon: false,
  82. yzmStatus: 'login-btn-disabled',
  83. timeLeft: 60, // 初始倒计时时间(秒)
  84. intervalId: null, // 定时器ID
  85. isDisabled: false, // 按钮是否禁用
  86. buttonText: '获取验证码', // 按钮文本
  87. })
  88. const sliderData = reactive({})
  89. function showDl() {
  90. dlRef.value.open('bottom');
  91. }
  92. function closeDl() {
  93. loginData.phoneNumber = null;
  94. loginData.yzmNumber = null;
  95. loginData.clearYzmIcon = false;
  96. loginData.yzmStatus = 'login-btn-disabled';
  97. loginData.timeLeft = 60;
  98. loginData.intervalId = null;
  99. loginData.isDisabled = false;
  100. loginData.buttonText = '';
  101. config.value = null;
  102. isAgreed.value = false;
  103. dlRef.value.close();
  104. }
  105. // 返回
  106. function handleBack() {
  107. closeDl();
  108. }
  109. // 手机号校验规则
  110. const validatePhoneNumber = (value) => {
  111. const phoneRegex = /^1[3-9]\d{9}$/;
  112. if (phoneRegex.test(value)) {
  113. loginData.telStatus = 'login-btn-normal';
  114. } else {
  115. loginData.telStatus = 'login-btn-disabled';
  116. }
  117. }
  118. const validatePhone = (value) => {
  119. const phoneRegex = /^1[3-9]\d{9}$/;
  120. return phoneRegex.test(value)
  121. }
  122. function clearTelInput(event) {
  123. if (event.detail.value.length > 0) {
  124. loginData.clearTelIcon = true;
  125. validatePhoneNumber(event.detail.value);
  126. } else {
  127. loginData.clearTelIcon = false;
  128. }
  129. }
  130. function clearTel() {
  131. loginData.phoneNumber = '';
  132. loginData.telStatus = 'login-btn-disabled';
  133. loginData.clearTelIcon = false;
  134. }
  135. function clearYzmInput(event) {
  136. if (event.detail.value.length > 0) {
  137. loginData.clearYzmIcon = true;
  138. loginData.yzmStatus = 'login-btn-normal';
  139. } else {
  140. loginData.clearYzmIcon = false;
  141. loginData.yzmStatus = 'login-btn-disabled';
  142. }
  143. }
  144. function clearYzm() {
  145. this.loginData.yzmNumber = '';
  146. this.loginData.yzmStatus = 'login-btn-disabled';
  147. this.loginData.clearYzmIcon = false;
  148. }
  149. // 登录
  150. function handleLogin() {
  151. // 用户名
  152. if (!loginData.phoneNumber) {
  153. toast('请输入手机号')
  154. return;
  155. }
  156. // 正确手机号
  157. if (!validatePhone(loginData.phoneNumber)) {
  158. toast('请输入正确手机号')
  159. return;
  160. }
  161. // 验证码
  162. if (!loginData.yzmNumber) {
  163. toast('请输入验证码')
  164. return;
  165. }
  166. // 协议
  167. if (!isAgreed.value) {
  168. agreeDialogRef.value.handleShow()
  169. return;
  170. }
  171. let req = {
  172. tel: loginData.phoneNumber,
  173. code: loginData.yzmNumber,
  174. }
  175. telBind(req).then(res => {
  176. if (res.code == 0 && res.data) {
  177. const auth = cacheManager.get('auth');
  178. auth.userName = loginData.phoneNumber;
  179. // 更新用户信息
  180. cacheManager.set('auth', res.data)
  181. // 返回重新支付
  182. handleBack();
  183. emits('success', auth)
  184. }
  185. })
  186. }
  187. // 更新协议
  188. function handleUpdateAgree() {
  189. isAgreed.value = !isAgreed.value
  190. }
  191. // 协议跳转
  192. function agreeBtn(data) {
  193. agreeType.value = data;
  194. agreeContentDialogRef.value.handleShow();
  195. }
  196. function confirmBtn() {
  197. isAgreed.value = true;
  198. handleLogin();
  199. }
  200. // 图形验证码
  201. function showCaptcha() {
  202. captcha.value.showCaptcha();
  203. }
  204. function startCountdown() {
  205. if (!loginData.phoneNumber) {
  206. toast('请输入手机号')
  207. return;
  208. }
  209. if (loginData.buttonText === '重新发送' ||loginData.buttonText === '获取验证码') {
  210. showCaptcha();
  211. return;
  212. }
  213. loginData.isDisabled = true;
  214. loginData.buttonText = `重新发送(${loginData.timeLeft}S)`;
  215. // 清除之前的定时器(如果有)
  216. if (loginData.intervalId) {
  217. clearInterval(loginData.intervalId);
  218. loginData.intervalId = null;
  219. }
  220. // 设置新的定时器
  221. loginData.intervalId = setInterval(() => {
  222. loginData.timeLeft--;
  223. if (loginData.timeLeft <= 0) {
  224. clearInterval(loginData.intervalId);
  225. loginData.intervalId = null;
  226. loginData.timeLeft = 60; // 重置倒计时
  227. loginData.isDisabled = false;
  228. loginData.buttonText = '重新发送';
  229. } else {
  230. loginData.buttonText = `重新发送(${loginData.timeLeft}S)`;
  231. }
  232. }, 1000);
  233. }
  234. function getYzmBtn() {
  235. let req = {
  236. phone: loginData.phoneNumber,
  237. captchaOutput: sliderData.captcha_output,
  238. genTime: sliderData.gen_time,
  239. lotNumber: sliderData.lot_number,
  240. passToken: sliderData.pass_token,
  241. }
  242. sendCode(req).then(res => {})
  243. }
  244. function captchaSuccess(result) {
  245. startCountdown();
  246. Object.assign(sliderData, result)
  247. getYzmBtn();
  248. }
  249. function captchaError(e) {
  250. toast(JSON.stringify(e))
  251. }
  252. function captchaFail() {
  253. toast('验证失败!')
  254. }
  255. function captchaReady() {}
  256. function captchaClose() {}
  257. defineExpose({
  258. showDl
  259. })
  260. </script>
  261. <style>
  262. </style>