CardHandle.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. <template>
  2. <u-modal v-model:show="showDialog" class="card-handle-dialog" :title="t('card.vaildate.v42')"
  3. :show-confirm-button="false" :show-cancel-button="false" :close-on-click-overlay="false" @closed="handleClose">
  4. <view class="card-handle-dialog-content">
  5. <u-form ref="formRef" :model="form" class="payment-form">
  6. <view class="code-input-label">{{ t("newSignup.item9") }}</view>
  7. <view class="code-input-wrapper">
  8. <view class="code-input">
  9. <cwg-input v-model:value="form.emailCode" fkey="emailCode" type="text" :required="true"
  10. :placeholder="t('newSignup.item10')" @change="handleChange" />
  11. </view>
  12. <view class="get-code-btn">
  13. <view class="cwg-button ok-button">
  14. <u-button type="primary" block @click="handleGetCode">{{
  15. getCodeString
  16. }}</u-button>
  17. </view>
  18. </view>
  19. </view>
  20. <cwg-input v-if="cvv" v-model:value="cvv" fkey="cvv" type="text" label="CVV" :readonly="true"
  21. :disabled="true" />
  22. </u-form>
  23. <view class="dialog-footer">
  24. <view v-if="!cvv" class="cwg-button ok-button">
  25. <u-button type="primary" block @click="handleConfirm">{{
  26. t("card.vaildate.v42")
  27. }}</u-button>
  28. </view>
  29. <view v-else class="cwg-button ok-button">
  30. <u-button type="primary" block @click="cardCopy">{{
  31. t("card.vaildate.v43")
  32. }}</u-button>
  33. </view>
  34. <view class="cwg-button no-button">
  35. <u-button type="default" block @click="handleClose">{{
  36. t("card.Btn.Cancel")
  37. }}</u-button>
  38. </view>
  39. </view>
  40. </view>
  41. </u-modal>
  42. </template>
  43. <script setup lang="ts">
  44. import { ref, onMounted, watch, onBeforeUnmount } from "vue";
  45. import { showToast } from "@/utils/toast";
  46. import { useI18n } from "vue-i18n";
  47. import { ucardApi } from "@/api/ucard";
  48. import errorIcon from "/static/images/error.png";
  49. import copyIcon from "/static/images/success.png";
  50. import { useEmailCountdown } from '@/hooks/useEmailCountdown';
  51. const {
  52. time,
  53. text: getCodeString,
  54. canSend,
  55. start,
  56. restore
  57. } = useEmailCountdown({ storageKey: 'cvvTimer' })
  58. const { t } = useI18n();
  59. const props = defineProps<{
  60. dialogInfoTradingAdd: boolean;
  61. formList: Record<string, any>;
  62. }>();
  63. const emit = defineEmits(["closeAdd"]);
  64. const showDialog = ref(false);
  65. const formRef = ref();
  66. const form = ref<{
  67. emailCode: string;
  68. password?: string;
  69. country?: string;
  70. email?: string;
  71. }>({
  72. emailCode: "",
  73. });
  74. const cvv = ref("");
  75. const rules = {
  76. emailCode: [
  77. {
  78. required: true,
  79. message: t("vaildate.code.empty"),
  80. },
  81. ],
  82. };
  83. // 监听 props 变化
  84. watch(
  85. () => props.dialogInfoTradingAdd,
  86. (newVal) => {
  87. showDialog.value = newVal;
  88. if (newVal) {
  89. initForm();
  90. restore()
  91. } else {
  92. handleClose();
  93. }
  94. },
  95. { immediate: true }
  96. );
  97. // 初始化表单
  98. function initForm() {
  99. if (props.formList) {
  100. form.value = {
  101. ...JSON.parse(JSON.stringify(props.formList)),
  102. emailCode: "",
  103. };
  104. }
  105. cvv.value = "";
  106. }
  107. // 发送邮箱验证码
  108. async function sendEmailCode() {
  109. try {
  110. if (!form.value.country) {
  111. showToast(t("vaildate.country.empty"));
  112. return false;
  113. }
  114. if (!form.value.email) {
  115. showToast(t("vaildate.email.empty"));
  116. return false;
  117. }
  118. const res = await ucardApi.sendEmailCode({
  119. ...form.value,
  120. });
  121. if (res.code === 200) {
  122. showToast(t("Msg.CodeSuccess"));
  123. start();
  124. return true;
  125. } else {
  126. showToast(t("Msg.CodeFail"));
  127. return false;
  128. }
  129. } catch (error: any) {
  130. console.log(error, 12);
  131. showToast(t("Msg.CodeFail"));
  132. return false;
  133. }
  134. }
  135. // 获取验证码按钮点击
  136. async function handleGetCode() {
  137. if (!canSend.value) {
  138. return;
  139. }
  140. cvv.value = "";
  141. await sendEmailCode();
  142. }
  143. // 表单字段变化
  144. function handleChange(value: any) {
  145. if (value.key === "emailCode") {
  146. form.value.emailCode = value.value;
  147. }
  148. }
  149. // 确认获取 CVV
  150. async function handleConfirm() {
  151. try {
  152. await formRef.value?.validate();
  153. if (!form.value.emailCode) {
  154. // showToast(t('vaildate.code.empty'))
  155. return;
  156. }
  157. const res = await ucardApi.getCvvCode({
  158. ...form.value,
  159. } as any);
  160. if (res.code === 200) {
  161. cvv.value = res.data;
  162. } else {
  163. showToast(res.msg);
  164. cvv.value = "";
  165. }
  166. } catch (error: any) {
  167. if (Array.isArray(error) && error.length > 0) {
  168. showToast({
  169. message: error[0].message,
  170. icon: errorIcon,
  171. className: "custom-toast",
  172. });
  173. } else {
  174. showToast({
  175. message: error?.message,
  176. icon: errorIcon,
  177. className: "custom-toast",
  178. });
  179. }
  180. }
  181. }
  182. // 复制 CVV
  183. function cardCopy() {
  184. let title = t("common.copy2");
  185. uni.setClipboardData({
  186. data: cvv.value,
  187. success: () => {
  188. uni.showToast({
  189. title,
  190. });
  191. },
  192. });
  193. }
  194. // 降级复制方案
  195. function fallbackCopy() {
  196. const textarea = document.createElement("textarea");
  197. textarea.value = cvv.value;
  198. textarea.setAttribute("readonly", "");
  199. textarea.style.position = "absolute";
  200. textarea.style.left = "-9999px";
  201. document.body.appendChild(textarea);
  202. textarea.select();
  203. try {
  204. document.execCommand("copy");
  205. showToast({
  206. message: t("card.Msg.m8"),
  207. icon: copyIcon,
  208. className: "custom-toast",
  209. });
  210. } catch (_err) {
  211. showToast({
  212. message: t("card.Msg.m9"),
  213. icon: errorIcon,
  214. className: "custom-toast",
  215. });
  216. }
  217. document.body.removeChild(textarea);
  218. }
  219. // 关闭对话框
  220. function handleClose() {
  221. showDialog.value = false;
  222. emit("closeAdd", false);
  223. restore()
  224. }
  225. </script>
  226. <style scoped lang="scss">
  227. @import "@/uni.scss";
  228. .card-handle-dialog {
  229. width: 100%;
  230. :deep(.u-popup__content) {
  231. width: 90% !important;
  232. }
  233. :deep(.u-modal) {
  234. width: 100% !important;
  235. }
  236. .card-handle-dialog-content {
  237. padding: px2rpx(44) px2rpx(2);
  238. .no-button {
  239. width: 100%;
  240. margin: px2rpx(12) 0;
  241. .u-button {
  242. background-color: #ffbdc8 !important;
  243. }
  244. }
  245. .ok-button {
  246. margin: px2rpx(4) 0;
  247. /* background-color: #EA002A; */
  248. }
  249. }
  250. .code-input-label {
  251. font-size: var(--font-size-16);
  252. line-height: px2rpx(44);
  253. letter-spacing: px2rpx(1);
  254. color: #474747;
  255. }
  256. .code-input-wrapper {
  257. position: relative;
  258. display: flex;
  259. align-items: center;
  260. }
  261. .code-input {
  262. flex: 1;
  263. }
  264. .get-code-btn {
  265. min-width: px2rpx(100);
  266. margin-bottom: px2rpx(12);
  267. margin-left: px2rpx(10);
  268. .cwg-button .u-button {
  269. border-radius: px2rpx(8);
  270. }
  271. }
  272. }
  273. </style>