cwg-email-code-popup.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. <template>
  2. <cwg-popup v-model:visible="visible" type="center" :mask-click="false" :show-footers="true"
  3. :title="t('signup.form.code')">
  4. <view class="popup-content">
  5. <view class="card-handle-dialog-content">
  6. <uni-forms ref="formRef1" :model="form" :rules="rules" label-position="top" validate-trigger="submit"
  7. :label-width="200" class="base-info-form">
  8. <view class="code-input-wrapper">
  9. <view class="code-input">
  10. <uni-forms-item :label="t('signup.form.code')" name="emailCode">
  11. <uni-easyinput type="number" v-model="form.emailCode" :placeholder="t('signup.form.code')" />
  12. </uni-forms-item>
  13. </view>
  14. <view class="get-code-btn">
  15. <view class="cwg-button ok-button">
  16. <button type="primary" block :disabled="!canSend || isLoading" @click="handleGetCode">{{
  17. getCodeString
  18. }}</button>
  19. </view>
  20. </view>
  21. </view>
  22. </uni-forms>
  23. </view>
  24. </view>
  25. <template #footer>
  26. <button @click="visible = false">{{ t('Btn.Cancel') }}</button>
  27. <button type="primary" @click="tosubmitConfirm">{{ t('Btn.Confirm') }}</button>
  28. </template>
  29. </cwg-popup>
  30. </template>
  31. <script setup lang="ts">
  32. import { ref, computed, onBeforeUnmount, watch } from "vue";
  33. import { showToast } from "@/utils/toast";
  34. import { useI18n } from "vue-i18n";
  35. import { useEmailCountdown } from '@/hooks/useEmailCountdown';
  36. const {
  37. time,
  38. text: getCodeString,
  39. canSend,
  40. start,
  41. restore
  42. } = useEmailCountdown({ storageKey: 'emailCodeTimer' })
  43. const { t, locale } = useI18n();
  44. const props = defineProps({
  45. visible: {
  46. type: Boolean,
  47. default: false
  48. },
  49. email: {
  50. type: String,
  51. default: ''
  52. },
  53. country: {
  54. type: String,
  55. default: ''
  56. },
  57. api: {
  58. type: Function,
  59. default: () => { }
  60. }
  61. });
  62. const emit = defineEmits(['update:visible', 'confirm']);
  63. // Watch for changes in visible prop and emit update event
  64. const visible = computed({
  65. get: () => props.visible,
  66. set: (value: boolean) => emit('update:visible', value)
  67. });
  68. const formRef1 = ref(null);
  69. const isLoading = ref(false);
  70. const form = ref<{
  71. emailCode: string;
  72. }>({
  73. emailCode: "",
  74. });
  75. const rules = computed(() => ({
  76. emailCode: {
  77. rules: [
  78. {
  79. required: true,
  80. errorMessage: t('vaildate.code.empty'),
  81. },
  82. {
  83. validateFunction: (rule, value, data, callback) => {
  84. // 空值处理:直接报格式错误
  85. if (!value) {
  86. callback(t('vaildate.code.empty'))
  87. return false
  88. }
  89. if (!/^\d{6}$/.test(value)) {
  90. callback(t('vaildate.code.empty'))
  91. return false
  92. }
  93. return true
  94. },
  95. },
  96. ],
  97. },
  98. }))
  99. watch(locale, () => {
  100. formRef1.value?.clearValidate()
  101. })
  102. // 发送邮箱验证码
  103. async function sendEmailCode() {
  104. try {
  105. isLoading.value = true;
  106. const res = await props.api({
  107. email: props.email || undefined,
  108. country: props.country || undefined
  109. });
  110. if (res.code === 200) {
  111. showToast(t("Msg.CodeSuccess"));
  112. start();
  113. return true;
  114. } else {
  115. showToast(t("Msg.CodeFail"));
  116. return false;
  117. }
  118. } catch (error: any) {
  119. showToast(t("Msg.CodeFail"));
  120. return false;
  121. } finally {
  122. isLoading.value = false;
  123. }
  124. }
  125. // 获取验证码按钮点击
  126. async function handleGetCode() {
  127. if (!canSend.value || isLoading.value) {
  128. return;
  129. }
  130. await sendEmailCode();
  131. }
  132. // 确认提交
  133. const tosubmitConfirm = async () => {
  134. if (formRef1.value) {
  135. try {
  136. await formRef1.value.validate();
  137. visible.value = false;
  138. console.log(form.value.emailCode, 12121);
  139. emit('confirm', form.value.emailCode);
  140. } catch (error) {
  141. if (error instanceof Array) {
  142. showToast(error[0].errorMessage);
  143. return
  144. } else {
  145. showToast(t("Msg.CodeFail"));
  146. }
  147. }
  148. }
  149. }
  150. // 组件卸载时恢复倒计时
  151. onBeforeUnmount(() => {
  152. restore();
  153. });
  154. </script>
  155. <style scoped lang="scss">
  156. @import "@/uni.scss";
  157. .popup-content {
  158. width: 100%;
  159. }
  160. .card-handle-dialog-content {
  161. width: 100%;
  162. .code-input-label {
  163. font-size: px2rpx(16);
  164. line-height: px2rpx(44);
  165. letter-spacing: px2rpx(1);
  166. color: #474747;
  167. }
  168. .code-input-wrapper {
  169. position: relative;
  170. display: flex;
  171. align-items: flex-end;
  172. gap: px2rpx(10);
  173. }
  174. .code-input {
  175. flex: 1;
  176. }
  177. .get-code-btn {
  178. min-width: px2rpx(120);
  179. display: flex;
  180. align-items: flex-end;
  181. margin-bottom: px2rpx(16);
  182. button {
  183. border-radius: px2rpx(8);
  184. font-size: px2rpx(14);
  185. line-height: px2rpx(35);
  186. height: 100%;
  187. height: px2rpx(35);
  188. }
  189. }
  190. /* 移动端适配 */
  191. @media screen and (max-width: 750px) {}
  192. :deep(.uni-forms-item) {
  193. margin-bottom: px2rpx(16);
  194. }
  195. :deep(.uni-easyinput) {
  196. border-radius: px2rpx(8);
  197. }
  198. }
  199. </style>