cwg-email-code-popup.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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 } 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 } = 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. // 发送邮箱验证码
  100. async function sendEmailCode() {
  101. try {
  102. isLoading.value = true;
  103. const res = await props.api({
  104. email: props.email || undefined,
  105. country: props.country || undefined
  106. });
  107. if (res.code === 200) {
  108. showToast(t("Msg.CodeSuccess"));
  109. start();
  110. return true;
  111. } else {
  112. showToast(t("Msg.CodeFail"));
  113. return false;
  114. }
  115. } catch (error: any) {
  116. showToast(t("Msg.CodeFail"));
  117. return false;
  118. } finally {
  119. isLoading.value = false;
  120. }
  121. }
  122. // 获取验证码按钮点击
  123. async function handleGetCode() {
  124. if (!canSend.value || isLoading.value) {
  125. return;
  126. }
  127. await sendEmailCode();
  128. }
  129. // 确认提交
  130. const tosubmitConfirm = async () => {
  131. if (formRef1.value) {
  132. try {
  133. await formRef1.value.validate();
  134. visible.value = false;
  135. console.log(form.value.emailCode, 12121);
  136. emit('confirm', form.value.emailCode);
  137. } catch (error) {
  138. if (error instanceof Array) {
  139. showToast(error[0].errorMessage);
  140. return
  141. } else {
  142. showToast(t("Msg.CodeFail"));
  143. }
  144. }
  145. }
  146. }
  147. // 组件卸载时恢复倒计时
  148. onBeforeUnmount(() => {
  149. restore();
  150. });
  151. </script>
  152. <style scoped lang="scss">
  153. @import "@/uni.scss";
  154. .popup-content {
  155. width: 100%;
  156. }
  157. .card-handle-dialog-content {
  158. width: 100%;
  159. .code-input-label {
  160. font-size: px2rpx(16);
  161. line-height: px2rpx(44);
  162. letter-spacing: px2rpx(1);
  163. color: #474747;
  164. }
  165. .code-input-wrapper {
  166. position: relative;
  167. display: flex;
  168. align-items: flex-end;
  169. gap: px2rpx(10);
  170. }
  171. .code-input {
  172. flex: 1;
  173. }
  174. .get-code-btn {
  175. min-width: px2rpx(120);
  176. display: flex;
  177. align-items: flex-end;
  178. margin-bottom: px2rpx(16);
  179. button {
  180. border-radius: px2rpx(8);
  181. font-size: px2rpx(14);
  182. height: 100%;
  183. min-height: px2rpx(35);
  184. }
  185. }
  186. /* 移动端适配 */
  187. @media screen and (max-width: 750px) {}
  188. :deep(.uni-forms-item) {
  189. margin-bottom: px2rpx(16);
  190. }
  191. :deep(.uni-easyinput) {
  192. border-radius: px2rpx(8);
  193. }
  194. }
  195. </style>