goLogin.vue 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <template>
  2. <uni-popup ref="dlRef" :animation="false" :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. nextTick
  55. } from "vue"
  56. import {
  57. toast
  58. } from "@/utils/common";
  59. import {
  60. login,
  61. telBind,
  62. sendCode
  63. } from "@/api/login";
  64. import cacheManager from "@/utils/cacheManager";
  65. import agreeContentDialog from '@/pages/login/agreeContentDialog.vue';
  66. import agreeDialog from '@/pages/login/agreeDialog.vue'
  67. import captchaVue from "@/components/captcha4/index.vue";
  68. import ezyActiveVue from "@/components/ezyActive/ezyActive.vue"
  69. const emits = defineEmits(['success'])
  70. const dlRef = ref(null)
  71. const captcha = ref(null);
  72. const agreeType = ref(null);
  73. const agreeDialogRef = ref(null);
  74. const agreeContentDialogRef = ref(null);
  75. const isAgreed = ref(false);
  76. const config = ref({
  77. captchaId: "9d5837b0807b8de44da0de310a0b2813",
  78. });
  79. const loginData = reactive({
  80. phoneNumber: null,
  81. yzmNumber: null,
  82. clearYzmIcon: false,
  83. yzmStatus: 'login-btn-disabled',
  84. timeLeft: 60, // 初始倒计时时间(秒)
  85. intervalId: null, // 定时器ID
  86. isDisabled: false, // 按钮是否禁用
  87. buttonText: '获取验证码', // 按钮文本
  88. })
  89. const sliderData = reactive({})
  90. function showDl() {
  91. dlRef.value.open('bottom');
  92. loginData.buttonText = '获取验证码';
  93. clearInterval(loginData.intervalId);
  94. loginData.intervalId = null;
  95. }
  96. function closeDl() {
  97. clearInterval(loginData.intervalId);
  98. loginData.intervalId = null;
  99. loginData.phoneNumber = null;
  100. loginData.yzmNumber = null;
  101. loginData.clearYzmIcon = false;
  102. loginData.yzmStatus = 'login-btn-disabled';
  103. loginData.timeLeft = 60;
  104. loginData.intervalId = null;
  105. loginData.isDisabled = false;
  106. loginData.buttonText = '获取验证码';
  107. config.value = null;
  108. isAgreed.value = false;
  109. dlRef.value.close();
  110. }
  111. // 返回
  112. function handleBack() {
  113. closeDl();
  114. }
  115. // 手机号校验规则
  116. const validatePhoneNumber = (value) => {
  117. const phoneRegex = /^1[3-9]\d{9}$/;
  118. if (phoneRegex.test(value)) {
  119. loginData.telStatus = 'login-btn-normal';
  120. } else {
  121. loginData.telStatus = 'login-btn-disabled';
  122. }
  123. }
  124. const validatePhone = (value) => {
  125. const phoneRegex = /^1[3-9]\d{9}$/;
  126. return phoneRegex.test(value)
  127. }
  128. function clearTelInput(event) {
  129. if (event.detail.value.length > 0) {
  130. loginData.clearTelIcon = true;
  131. validatePhoneNumber(event.detail.value);
  132. } else {
  133. loginData.clearTelIcon = false;
  134. }
  135. }
  136. function clearTel() {
  137. loginData.phoneNumber = '';
  138. loginData.telStatus = 'login-btn-disabled';
  139. loginData.clearTelIcon = false;
  140. }
  141. function clearYzmInput(event) {
  142. if (event.detail.value.length > 0) {
  143. loginData.clearYzmIcon = true;
  144. loginData.yzmStatus = 'login-btn-normal';
  145. } else {
  146. loginData.clearYzmIcon = false;
  147. loginData.yzmStatus = 'login-btn-disabled';
  148. }
  149. }
  150. function clearYzm() {
  151. this.loginData.yzmNumber = '';
  152. this.loginData.yzmStatus = 'login-btn-disabled';
  153. this.loginData.clearYzmIcon = false;
  154. }
  155. // 登录
  156. function handleLogin() {
  157. // 用户名
  158. if (!loginData.phoneNumber) {
  159. toast('请输入手机号')
  160. return;
  161. }
  162. // 正确手机号
  163. if (!validatePhone(loginData.phoneNumber)) {
  164. toast('请输入正确手机号')
  165. return;
  166. }
  167. // 验证码
  168. if (!loginData.yzmNumber) {
  169. toast('请输入验证码')
  170. return;
  171. }
  172. // 协议
  173. if (!isAgreed.value) {
  174. agreeDialogRef.value.handleShow()
  175. return;
  176. }
  177. let req = {
  178. tel: loginData.phoneNumber,
  179. code: loginData.yzmNumber,
  180. }
  181. login(req).then(res => {
  182. if (res.code == 0 && res.data) {
  183. cacheManager.clearAll();
  184. nextTick(() => {
  185. cacheManager.set('auth', res.data)
  186. // 返回重新支付
  187. handleBack();
  188. emits('success', res.data)
  189. })
  190. }
  191. })
  192. }
  193. // 更新协议
  194. function handleUpdateAgree() {
  195. isAgreed.value = !isAgreed.value
  196. }
  197. // 协议跳转
  198. function agreeBtn(data) {
  199. agreeType.value = data;
  200. agreeContentDialogRef.value.handleShow();
  201. }
  202. function confirmBtn() {
  203. isAgreed.value = true;
  204. handleLogin();
  205. }
  206. // 图形验证码
  207. function showCaptcha() {
  208. captcha.value.showCaptcha();
  209. }
  210. function startCountdown() {
  211. if (!loginData.phoneNumber) {
  212. toast('请输入手机号')
  213. return;
  214. }
  215. if (loginData.buttonText === '重新发送' ||loginData.buttonText === '获取验证码') {
  216. showCaptcha();
  217. return;
  218. }
  219. loginData.isDisabled = true;
  220. loginData.buttonText = `(${loginData.timeLeft}S)`;
  221. // 清除之前的定时器(如果有)
  222. if (loginData.intervalId) {
  223. clearInterval(loginData.intervalId);
  224. loginData.intervalId = null;
  225. }
  226. // 设置新的定时器
  227. loginData.intervalId = setInterval(() => {
  228. loginData.timeLeft--;
  229. if (loginData.timeLeft <= 0) {
  230. clearInterval(loginData.intervalId);
  231. loginData.intervalId = null;
  232. loginData.timeLeft = 60; // 重置倒计时
  233. loginData.isDisabled = false;
  234. loginData.buttonText = '重新发送';
  235. } else {
  236. loginData.buttonText = `(${loginData.timeLeft}S)`;
  237. }
  238. }, 1000);
  239. }
  240. function getYzmBtn() {
  241. let req = {
  242. phone: loginData.phoneNumber,
  243. captchaOutput: sliderData.captcha_output,
  244. genTime: sliderData.gen_time,
  245. lotNumber: sliderData.lot_number,
  246. passToken: sliderData.pass_token,
  247. }
  248. sendCode(req).then(res => {}).catch(err => {
  249. loginData.buttonText = '重新发送';
  250. loginData.timeLeft = 60;
  251. clearInterval(loginData.intervalId);
  252. loginData.intervalId = null;
  253. })
  254. }
  255. function captchaSuccess(result) {
  256. loginData.buttonText = `(${loginData.timeLeft}S)`;
  257. startCountdown();
  258. Object.assign(sliderData, result)
  259. getYzmBtn();
  260. }
  261. function captchaError(e) {
  262. toast(JSON.stringify(e))
  263. }
  264. function captchaFail() {
  265. toast('验证失败!')
  266. }
  267. function captchaReady() {}
  268. function captchaClose() {}
  269. defineExpose({
  270. showDl
  271. })
  272. </script>
  273. <style>
  274. </style>