瀏覽代碼

充值提现

nieyuge 2 年之前
父節點
當前提交
36b2e58cce
共有 8 個文件被更改,包括 1427 次插入33 次删除
  1. 2 0
      package.json
  2. 434 0
      src/components/popup-withdraw.vue
  3. 30 32
      src/pages/currency/detail.vue
  4. 321 0
      src/pages/topup/info.vue
  5. 448 0
      src/pages/withdraw/info.vue
  6. 52 0
      src/pages/withdraw/paypal.vue
  7. 15 0
      src/router/index.js
  8. 125 1
      yarn.lock

+ 2 - 0
package.json

@@ -16,11 +16,13 @@
     "@vue/composition-api": "^1.7.0",
     "ant-design-vue": "^3.2.11",
     "axios": "^0.27.2",
+    "clipboard": "^2.0.11",
     "core-js": "^3.8.3",
     "element-plus": "2.1.10",
     "moment": "^2.29.4",
     "postcss-import": "^15.0.0",
     "postcss-url": "^10.1.3",
+    "qrcode": "^1.5.1",
     "vue": "^3.2.13",
     "vue-router": "^4.1.2"
   },

+ 434 - 0
src/components/popup-withdraw.vue

@@ -0,0 +1,434 @@
+<template>
+    <div class="withdraw-wrapper">
+        <template v-if="!isSubmit">
+            <div class="content">
+                <div class="logo-wrapper">
+                    <div class="title">Withdraw to</div>
+                    <img class="icon" :src="
+                        require('@/assets/svg/icon-withdraw-paypal-logo.svg')
+                    " />
+                </div>
+
+                <div class="form-wrapper">
+                    <div class="form-item">
+                        <div class="label">PayPal account</div>
+                        <div class="input-wrapper">
+                            <input type="text" v-model="requestWithdrawParams.withdrawReceiveAccount"
+                                placeholder="Enter PayPal account" />
+                        </div>
+                    </div>
+                    <div class="form-item">
+                        <div class="label">Withdrawal amount<span class="msg">(${{
+                                walletWithdrawConfig.withdrawPerMinAmount
+                        }} minimum)</span></div>
+                        <div class="input-wrapper amount-wrapper">
+                            <input type="text" v-model="requestWithdrawParams.amountValue" placeholder="$0"
+                                style="width: 220px" @input="onAmountInput" />
+                            <div @click="withdrawalAll" class="withdrawal-all-btn">
+                                Withdrawal All
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="error-msg">
+                        <template v-if="showWithdrawError">
+                            The minimum withdrawal amount is ${{ walletWithdrawConfig.withdrawPerMinAmount }} USD
+                        </template>
+                        <template v-if="showWithdrawIptError">
+                            The withdrawal amount exceeds the total account balance of ${{ canWithdrawBalance }} USD
+                        </template>
+                    </div>
+                </div>
+
+                <div class="bottom-msg">
+                    <div class="top">
+                        <template v-if="!calcReq">
+                            final amount
+                            <span>${{
+                                    finalWithdrawalAmount > 0
+                                        ? finalWithdrawalAmount
+                                        : 0
+                            }}
+                            </span>
+                        </template>
+                        <template v-else>
+                            calculating
+                        </template>
+                    </div>
+                    <div>{{ walletWithdrawConfig.withdrawFeeDesc }}</div>
+                </div>
+            </div>
+            <div @click="withdraw" class="confirm-btn">
+                <img class="icon-loading" v-if="withdrawIng" :src="require('@/assets/svg/icon-btn-loading.svg')" />
+                Confirm
+            </div>
+        </template>
+        <template v-else>
+            <div class="withdraw-status">
+                <img :src="require('@/assets/svg/icon-withdraw-status.svg')" alt="" />
+                <div>
+                    <div class="title">Submitted successfully</div>
+                    <div class="desc">
+                        <!-- Please wait for a while,<br />
+                        then check the balance with paypal -->
+                        Estimate in 24 hours
+                    </div>
+                </div>
+            </div>
+            <div class="confirm-btn" @click="doneWithdraw">
+                Done
+            </div>
+        </template>
+    </div>
+</template>
+
+<script setup>
+/* eslint-disable */
+import { defineProps, defineEmits, ref, onMounted, watch, computed, inject } from "vue";
+import { withdrawRequest, getWithdrawConfig } from "@/http/account";
+import { withdrawCalcFee } from "@/http/pay";
+import { debounce } from "@/uilts/help"
+
+const props = defineProps({
+    amountValue: {
+        type: Number,
+        default: 0,
+    }
+});
+let withdraw_info = inject('withdraw_info')
+
+console.log('withdraw_info', withdraw_info)
+let requestWithdrawParams = ref({
+    amountValue: "",
+    currencyCode: "USD",
+    withdrawNetwork: 'paypal', // 1: paypal
+    withdrawReceiveAccount: "",
+});
+
+let canWithdrawBalance = ref(withdraw_info.paypal.amount_value);
+
+let showWithdrawError = ref(false);
+let showWithdrawIptError = ref(false);
+let isSubmit = ref(false);
+let withdrawIng = ref(false);
+
+let walletWithdrawConfig = ref({
+    withdrawPerMinAmount: 1,
+    withdrawUSDSwitch: "",
+    withdrawFeeDesc: ''
+});
+
+let finalWithdrawalAmount = ref('');
+let calcReq = ref(false);
+
+onMounted(() => {
+    queryWithdrawConfig()
+});
+/**
+ * 获取提现配置
+ */
+const queryWithdrawConfig = () => {
+    getWithdrawConfig({
+        params: {
+            currencyCode: withdraw_info.currency_code || 'USD',
+            withdrawNetwork: 'paypal'
+        },
+    }).then((res) => {
+        if (res.code == 0) {
+            walletWithdrawConfig.value = res.data;
+        }
+        console.log('queryWithdrawConfig', walletWithdrawConfig.value);
+    });
+};
+
+const emits = defineEmits("back");
+const back = () => {
+    if (isSubmit.value) {
+        isSubmit.value = false;
+    } else {
+        emits("back", {});
+    }
+};
+
+const doneWithdraw = () => {
+    isSubmit.value = false;
+    emits("back", {});
+};
+
+
+const withdrawalAll = () => {
+    console.log(canWithdrawBalance.value);
+    showWithdrawIptError.value = false;
+    requestWithdrawParams.value.amountValue =
+        canWithdrawBalance.value;
+    setWithdrawIptStatus(canWithdrawBalance.value);
+    withdrawCalcAmount();
+};
+
+const withdrawCalcAmount = () => {
+    calcReq.value = true;
+    withdrawCalcFee({
+        params: {
+            amountValue: requestWithdrawParams.value.amountValue,
+            currencyCode: requestWithdrawParams.value.currencyCode,
+            withdrawNetwork: 'paypal'
+        }
+    }).then(res => {
+        calcReq.value = false;
+        if (res.code == 0) {
+            finalWithdrawalAmount.value = res.data.finalAmountValue;
+        }
+    }).catch(err => {
+        calcReq.value = false;
+    })
+}
+
+const withdrawCalcAmountDebounce = debounce(function () {
+    withdrawCalcAmount();
+}, 300)
+
+/**
+ * 提现
+ */
+const withdraw = () => {
+    console.log("requestWithdrawParams.value", requestWithdrawParams.value);
+    if (withdrawIng.value || calcReq.value || showWithdrawError.value || showWithdrawIptError.value) {
+        return;
+    }
+
+    let params = {
+        ...requestWithdrawParams.value,
+    };
+    if (!params.amountValue || !params.withdrawReceiveAccount) {
+        return;
+    }
+    params.withdrawReceiveAccount = params.withdrawReceiveAccount.replace(/\s*/g, "");
+    params.amountValue = params.amountValue;
+    if (+params.amountValue > +canWithdrawBalance.value) {
+        return;
+    }
+    withdrawIng.value = true;
+    withdrawRequest({
+        params,
+    }).then((res) => {
+        withdrawIng.value = false;
+        if (res.code == 0) {
+            canWithdrawBalance.value =
+                canWithdrawBalance.value - params.amountValue;
+            requestWithdrawParams.value = {
+                amountValue: "",
+                currencyCode: "USD",
+                withdrawNetwork: 'paypal', //  paypal
+                withdrawReceiveAccount: "",
+            };
+            isSubmit.value = true;
+        } else {
+            console.log(res);
+        }
+    }).catch((err) => {
+        console.log(err);
+    });
+};
+
+const onAmountBlur = () => {
+    withdrawCalcAmount();
+}
+
+const onAmountInput = () => {
+    //限制输入数字 小数点俩位
+    let value = requestWithdrawParams.value.amountValue;
+    value = value
+        .replace(/[^\d.]/g, "")
+        .replace(/\.{2,}/g, ".")
+        .replace(".", "$#$")
+        .replace(/\./g, "")
+        .replace("$#$", ".")
+        .replace(/^(-)*(\d+)\.(\d\d).*$/, "$1$2.$3")
+        .replace(/^\./g, "");
+    requestWithdrawParams.value.amountValue = value;
+
+    setWithdrawIptStatus(value);
+
+    // 输入金额大于可提现金额
+    if (+value > +canWithdrawBalance.value) {
+        if (!showWithdrawError.value) {
+            showWithdrawIptError.value = true;
+        }
+    } else {
+        showWithdrawIptError.value = false;
+    }
+
+    withdrawCalcAmountDebounce();
+
+    return value;
+};
+
+const setWithdrawIptStatus = (amount) => {
+    //显示tips
+    if (
+        +amount > 0 &&
+        +amount < +walletWithdrawConfig.value.withdrawPerMinAmount
+    ) {
+        showWithdrawError.value = true;
+    } else {
+        showWithdrawError.value = false;
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.withdraw-wrapper {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    overflow: hidden;
+
+    .nav-bar {
+        padding: 14px;
+        box-sizing: border-box;
+        display: flex;
+        align-items: center;
+        font-weight: 500;
+        font-size: 13px;
+
+        .icon {
+            width: 16px;
+            margin-right: 6px;
+            cursor: pointer;
+        }
+    }
+
+    .content {
+        padding: 0 20px;
+        box-sizing: border-box;
+
+        .logo-wrapper {
+            .title {
+                margin-top: 13px;
+                font-size: 14px;
+                color: #5b5b5b;
+            }
+
+            .icon {
+                width: 111px;
+                height: 56px;
+                margin-bottom: 20px;
+            }
+        }
+
+        .form-wrapper {
+            .form-item {
+                margin-bottom: 30px;
+
+                .label {
+                    font-size: 14px;
+                    color: #5b5b5b;
+                    margin-bottom: 8px;
+
+                    .msg {
+                        font-weight: 400;
+                        font-size: 14px;
+                        color: #FFA621;
+                    }
+                }
+
+                .input-wrapper {
+                    display: flex;
+                    align-items: center;
+                    justify-content: space-between;
+                    border: 1px solid #e8e8e8;
+                    border-radius: 8px;
+
+                    .withdrawal-all-btn {
+                        font-weight: 500;
+                        font-size: 13px;
+                        color: #1D9BF0;
+                        padding-right: 10px;
+                        cursor: pointer;
+                    }
+
+                    input {
+                        width: 100%;
+                        height: 48px;
+                        border: none;
+                        border-radius: 8px;
+                        padding: 0 16px;
+                        box-sizing: border-box;
+                        outline: none;
+                    }
+                }
+            }
+
+            .error-msg {
+                font-size: 13px;
+                color: #ff0000;
+                margin-top: -15px;
+                height: 38px;
+            }
+        }
+
+        .bottom-msg {
+            font-size: 13px;
+            color: #9d9d9d;
+            text-align: right;
+            margin-top: 52px;
+
+            .top {
+                height: 22px;
+
+                span {
+                    font-weight: 500;
+                    font-size: 15px;
+                    color: #000000;
+                }
+            }
+        }
+    }
+
+    .withdraw-status {
+        text-align: center;
+
+        img {
+            margin-top: 40px;
+            margin-bottom: 34px;
+        }
+
+        .title {
+            font-weight: 500;
+            font-size: 20px;
+            margin-bottom: 10px;
+        }
+
+        .desc {
+            font-size: 15px;
+            color: rgba($color: #000000, $alpha: 0.5);
+        }
+    }
+
+    .confirm-btn {
+        width: 335px;
+        height: 60px;
+        text-align: center;
+        line-height: 60px;
+        border-radius: 100px;
+        background: #1D9BF0;
+        font-weight: 600;
+        font-size: 18px;
+        color: #fff;
+        position: absolute;
+        left: 50%;
+        bottom: 35px;
+        transform: translateX(-50%);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        cursor: pointer;
+
+        .icon-loading {
+            width: 20px;
+            height: 20px;
+            margin-right: 3px;
+        }
+    }
+}
+</style>

+ 30 - 32
src/pages/currency/detail.vue

@@ -33,7 +33,7 @@
         </div>
         <div class="bottom">
             <template v-if="showSendBtn">
-                <div class="btn send-btn" v-if="enableRecharge == 1" @click="showSendGiveawayDialog(currencyInfo)">
+                <div class="btn send-btn" v-if="enableRecharge == 1 && isInApp === false" @click="showSendGiveawayDialog(currencyInfo)">
                     <img :src="require('@/assets/svg/icon-send-giveaway.svg')" />
                     Send Giveaway
                 </div>
@@ -72,7 +72,7 @@ import MESSAGE_ENUM from "@/uilts/messageCenter/messageEnum";
 import VHead from '@/components/v-head.vue'
 import currencySelect from "@/components/currency-select.vue";
 import inputActionSheet from "@/components/input-action-sheet.vue";
-import { getBit } from "@/uilts/help";
+import { getBit, isInApp } from "@/uilts/help";
 import { payCalcFee } from "@/http/pay";
 
 let currenciesData = ref([]);
@@ -131,15 +131,21 @@ const clickWithdraw = () => {
 }
 
 const withdrawHandle = (_params) => {
-    messageCenter.send({
-        info: {
-            actionType: MESSAGE_ENUM.IFRAME_WITHDRAW,
-            iframeId: iframeId,
-        },
-        data: {
-            params: _params
-        }
-    })
+    if (_params.chainInfo) {
+        _params.chainInfo = JSON.stringify(_params.chainInfo);
+    }
+    // jump
+    if (_params.currencyCode == 'USD') {
+        router.push({
+            name: 'pageWithdrawPaypal',
+            query: _params
+        })
+    } else {
+        router.push({
+            name: 'pageWithdrawInfo',
+            query: _params
+        })
+    }
 }
 
 const clickDeposit = () => {
@@ -180,14 +186,12 @@ const setDepositDesc = async () => {
 }
 
 const depositHandle = (_params) => {
-    messageCenter.send({
-        info: {
-            actionType: MESSAGE_ENUM.IFRAME_DEPOSIT,
-            iframeId: iframeId,
-        },
-        data: {
-            params: _params
-        }
+    if (_params.chainInfo) {
+        _params.chainInfo = JSON.stringify(_params.chainInfo);
+    }
+    router.push({
+        name: 'pageTopupInfo',
+        query: _params
     })
 };
 
@@ -304,21 +308,15 @@ const clickBack = () => {
 }
 
 const onMessage = () => {
-    // chrome.runtime.onMessage.addListener(msgListener)
+    let refresh = false;
+    messageCenter.listen(MESSAGE_ENUM.CONTENT_POPUP_PAGE_SHOW, (req) => {
+        refresh = router.currentRoute.value.query;
+        if (refresh && currencyInfo.value.tokenSymbol) {
+            onRefresh();
+        }
+    })
 }
 
-// const msgListener = (req) => {
-//     let refresh = false;
-//     switch (req.actionType) {
-//         case 'CONTENT_POPUP_PAGE_SHOW':
-//             refresh = router.currentRoute.value.query;
-//             if (refresh && currencyInfo.value.tokenSymbol) {
-//                 onRefresh();
-//             }
-//             break;
-//     }
-// }
-
 onMounted(() => {
     let { params = '{}' } = router.currentRoute.value.query;
     let { currencies = [], totalBalance = 0, totalUsdEstimateBalance = 0 } = JSON.parse(params);

+ 321 - 0
src/pages/topup/info.vue

@@ -0,0 +1,321 @@
+<template>
+    <!-- 公共组件 -->
+    <div class="info">
+        <v-head :title="'Deposit'"></v-head>
+        <!-- 内容 -->
+        <div class="content">
+            <div class="area-input-1">
+                <div class="token">
+                    <div class="title">Token</div>
+                    <div class="box">
+                        <img :src="top_up_info.icon_token" alt="">
+                        <span>{{ top_up_info.token_symbol }}</span>
+                    </div>
+                </div>
+
+                <div class="net">
+                    <div class="title">NetWork</div>
+                    <div class="box">
+                        <img :src="top_up_info.chainInfo.iconPath" alt="">
+                        <!-- <span>{{ top_up_info.token_chain }}</span> -->
+                        <span>{{top_up_info.chainInfo.chainName}}</span>
+                    </div>
+                </div>
+            </div>
+            <div class="info">
+                <canvas id="canvas"></canvas>
+                <div class="txt">{{ state.token_address }}</div>
+                <div class="copy-btn" :data-clipboard-text="state.token_address">Copy</div>
+            </div>
+
+            <div class="tips">
+                <div class="tips-icon">
+                    <img :src="require('@/assets/svg/icon-top-up-tips-warning.svg')" alt="">
+                </div>
+                <div class="tips-content">
+                    <p>Make sure you also selected </p>
+                    <p style="color: red;">{{top_up_info.chainInfo.chainName}} </p>
+                    <p>as the Network on the platform where you are withdrawing funds for this deposiit. Otherwise,
+                        you'll lose your assets.</p>
+                </div>
+            </div>
+        </div>
+
+        <!-- 底部 -->
+        <div class="footer">
+            <a-button class="confirm-btn" type="primary" shape="circle" :loading="state.loading" @click="clickDone">Done
+            </a-button>
+        </div>
+    </div>
+
+</template>
+
+<script setup>
+import { onMounted, reactive, inject } from "vue";
+import VHead from '@/components/v-head.vue'
+import { useRouter, useRoute } from "vue-router";
+import { getTokenRechargeAddress } from "@/http/pay";
+import { message } from 'ant-design-vue';
+import Report from "@/log-center/log";
+import { syncChainTokenRechargeRecord } from "@/http/publishApi";
+
+let route = useRoute()
+var router = useRouter()
+let _params = route.query;
+let top_up_info = reactive({
+    token: _params.currencyName || '',
+    token_chain: _params.tokenChain,
+    token_symbol: _params.tokenSymbol || '',
+    currency_code: _params.currencyCode,
+    icon_token: _params.iconPath || '',
+    icon_net: require('@/assets/svg/icon-BNB.svg'),
+    chainInfo: {
+        ...JSON.parse(_params.chainInfo)
+    }
+})
+var QRCode = require('qrcode')
+var ClipboardJS = require('clipboard')
+let state = reactive({
+    token_address: ''
+})
+const asyncTokenRechRecord = (cb) => {
+    syncChainTokenRechargeRecord({
+        params: {
+            currencyCode: top_up_info.currency_code
+        }
+    }).then(res => {
+        state.loading = false
+        switch (res.code.toString()) {
+            case "0":
+                router.back()
+                break
+            default:
+                // message.error(res.msg)
+                break
+        }
+    })
+}
+
+const clickDone = () => {
+    state.loading = true
+    asyncTokenRechRecord()
+}
+
+const createQRCode = (str) => {
+    var canvas = document.getElementById('canvas')
+    QRCode.toCanvas(canvas, str, {
+        width: 150,
+        height: 150,
+        scale: 10, color: {
+            dark: '#000', // 二维码前景颜色
+            light: '#F7F7F7' // 二维码背景颜色
+        }
+
+    }, function (error) {
+        if (error) console.error(error)
+        console.log('success!');
+    })
+}
+const copyToken = () => {
+    var clipboard = new ClipboardJS('.copy-btn');
+    clipboard.on('success', function (e) {
+        console.log(1)
+        message.success('copy success');
+        console.info('Action:', e.action);
+        console.info('Text:', e.text);
+        console.info('Trigger:', e.trigger);
+
+        e.clearSelection();
+    });
+
+    clipboard.on('error', function (e) {
+        message.error('copy error');
+        console.error('Action:', e.action);
+        console.error('Trigger:', e.trigger);
+    });
+}
+
+onMounted(() => {
+    Report.reportLog({
+        pageSource: Report.pageSource.denetSelector,
+        businessType: Report.businessType.pageView,
+    });
+    getTokenRechargeAddress({
+        params: {
+            "tokenChain": top_up_info.token_chain
+        },
+    }).then((res) => {
+        switch (res.code.toString()) {
+            case '0':
+                if (res.data && res.data.rechargeAddress) {
+                    state.token_address = res.data.rechargeAddress
+                    createQRCode(state.token_address)
+                    copyToken()
+                }
+                break;
+
+            default:
+                break;
+        }
+    })
+
+})
+
+
+</script>
+
+
+<style lang='scss' scoped>
+.info {
+    position: relative;
+    height: 100%;
+    overflow-y: auto;
+
+    .content {
+        padding: 12px 16px 0 16px;
+
+        .area-input-1 {
+
+
+            .token {}
+
+            .net {
+                margin-top: 12px;
+            }
+
+            .token,
+            .net {
+
+
+                .title {
+                    font-weight: 500;
+                    font-size: 12px;
+                    margin-bottom: 6px;
+                    color: #8B8B8B;
+                }
+
+                .box {
+                    border: 1px solid #DBDBDB;
+                    border-radius: 8px;
+                    height: 44px;
+                    display: flex;
+                    align-items: center;
+                    padding: 0 15px 0 10px;
+                    display: flex;
+                    flex-wrap: nowrap;
+                    justify-content: space-between;
+
+                    img {
+                        width: 20px;
+                        height: 20px;
+                    }
+
+                    .up {
+                        width: 13px;
+                        height: 12px;
+                        cursor: pointer;
+                    }
+
+                    span {
+                        flex: 1;
+                        margin-left: 6px;
+                        font-size: 14px;
+                        font-weight: 500;
+                    }
+                }
+            }
+        }
+
+        .info {
+            display: flex;
+            flex-wrap: wrap;
+            justify-content: center;
+            background: #F7F7F7;
+            border-radius: 18px;
+            padding-bottom: 20px;
+            margin-top: 12px;
+
+            canvas {
+                margin-top: 17px;
+            }
+
+            .txt {
+                margin-top: 5px;
+                font-weight: 400;
+                font-size: 15px;
+                width: 100%;
+                text-align: center;
+                word-break: break-all;
+            }
+
+            .copy-btn {
+                cursor: pointer;
+                margin-top: 5px;
+                width: 160px;
+                height: 40px;
+                line-height: 38px;
+                text-align: center;
+                font-size: 16px;
+                color: #1D9BF0;
+
+                border: 1px solid #1D9BF0;
+                border-radius: 100px;
+
+            }
+        }
+
+        .tips {
+            margin-top: 12px;
+            font-size: 13px;
+            height: 118;
+            overflow: auto;
+            background: #FCF5E5;
+            display: flex;
+            padding: 10px;
+            border-radius: 10px;
+
+            .tips-icon {
+
+                width: 30px;
+
+                img {
+                    width: 20px;
+                    height: 20px;
+                }
+            }
+
+            .tips-content {
+                flex: 1;
+            }
+
+            p {
+                margin: 0;
+                padding: 0;
+            }
+        }
+
+    }
+
+    .footer {
+        height: 80px;
+        width: 100%;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        // position: absolute;
+        // bottom: 0;
+
+        .confirm-btn {
+            width: 335px;
+            height: 46px;
+            text-align: center;
+            border-radius: 100px;
+            background: #1D9BF0;
+            font-weight: 600;
+            font-size: 18px;
+            color: #fff;
+            cursor: pointer;
+        }
+    }
+}
+</style>

+ 448 - 0
src/pages/withdraw/info.vue

@@ -0,0 +1,448 @@
+<template>
+    <!-- 公共组件 -->
+    <div class="info">
+        <v-head :title="'Withdraw'" :show_more="true" :show_help="true" :transactionsRouterParams="{
+          backUrl: 'back'
+        }"></v-head>
+        <!-- 内容 -->
+        <div class="content">
+            <div class="area-input-1">
+                <div class="token">
+                    <div class="title">Crypto</div>
+                    <div class="box">
+                        <img :src="withdraw_info.icon_token" alt="">
+                        <span>{{ withdraw_info.token_symbol }}</span>
+                    </div>
+                </div>
+
+                <div class="net">
+                    <div class="title">Withdrawal Network</div>
+                    <div class="box">
+                        <img :src="withdraw_info.chainInfo.iconPath" alt="">
+                        <!-- <span>{{ withdraw_info.token_chain }}</span> -->
+                        <span>{{withdraw_info.chainInfo.chainName}}</span>
+                        <!-- <img :src="require('@/assets/svg/icon-botton-up.svg')" alt="" class="up"> -->
+                    </div>
+                </div>
+            </div>
+
+            <div class="area-input-2">
+                <div class="title">Wallet Address</div>
+                <div class="box">
+                    <input type="text" placeholder="Click to enter" v-model="state.input_address" @input="inputText">
+                </div>
+            </div>
+
+            <div class="area-input-3">
+                <div class="title">
+                    <span>Withdrawal Amount</span>
+                    <span>Balance: {{ state.balance || 0 }}</span>
+                </div>
+                <div class="box">
+                    <input type="number" placeholder="0" @input="inputText" v-model="state.input_amount"
+                        :max="state.balance">
+                    <span @click="clickWithdrawalAll">Withdrawal All</span>
+                </div>
+                <div class="error">{{ state.error_msg }}</div>
+            </div>
+
+            <div class="tips">
+                <div class="tips-icon">
+                    <img :src="require('@/assets/svg/icon-top-up-tips-warning.svg')" alt="">
+                </div>
+                <div class="tips-content">
+                    <p>Select the same network on the platform where you are depositing funds to. Otherwise, you'll lose
+                        your
+                        assets.</p>
+                    <p>As the withdrawal is transferred out, you can check the transaction history.</p>
+                </div>
+            </div>
+        </div>
+        <!-- 底部 -->
+        <div class="footer">
+            <div class="left">
+                <div class="txt">Receive Amount</div>
+                <div class="money">{{ state.amount || 0 }} {{ state.currency_code }}</div>
+                <div class="txt"> Network Fee: <a-tooltip :title="state.fee_amount">{{ getBit(state.fee_amount) }}
+                    </a-tooltip> {{ withdraw_info.token_symbol }} </div>
+            </div>
+            <div class="right">
+                <div class="btn" @click="clickBtn" :class="{ enter: state.is_enter_state }">Confirm</div>
+            </div>
+        </div>
+    </div>
+
+</template>
+  
+<script setup>
+import VHead from '@/components/v-head.vue'
+import { useRouter, useRoute } from "vue-router";
+import { reactive, onMounted } from 'vue'
+import { getWithdrawConfig } from "@/http/account";
+import { withdrawCalcFee } from "@/http/pay";
+import Report from "@/log-center/log";
+import { getBit } from "@/uilts/help";
+
+let route = useRoute()
+let router = useRouter()
+let _params = route.query;
+console.log(333, _params)
+let withdraw_info = reactive({
+    icon_net: require('@/assets/svg/icon-BNB.svg'),
+    chainInfo: JSON.parse(_params.chainInfo),
+    source: 'home',
+    balance: _params.balance,
+    token_symbol: _params.tokenSymbol || '',
+    currency_name: _params.currencyName || '',
+    token_chain: _params.tokenChain || '',
+    currency_code: _params.currencyCode,
+    icon_token: _params.iconPath || '',
+})
+
+console.log(333, withdraw_info)
+
+let state = reactive({
+    input_address: '',
+    input_amount: '',
+    is_enter_state: false,
+    error_msg: 'Please enter wallet address'
+})
+
+const inputWithdrawCalcFee = () => {
+    withdrawCalcFee({
+        params: {
+            "amountValue": state.input_amount || 0,
+            "currencyCode": withdraw_info.currency_code,
+            "withdrawNetwork": withdraw_info.token_chain
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            if (res.data) {
+                state.currency_code = res.data.currencyCode || ''
+                state.fee_amount = res.data.feeAmountValue || 0
+                state.amount = res.data.finalAmountValue > 0 ? res.data.finalAmountValue : 0
+                state.is_enter_state = true
+                inputText('yes')
+            }
+        }
+
+    })
+}
+
+const inputText = (is_check_input) => {
+    if (is_check_input != 'yes') {
+        inputWithdrawCalcFee()
+    }
+    if (!state.withdraw_switch) {
+        return
+    }
+    if (!state.input_amount == undefined || state.input_amount < 0) {
+        state.input_amount = 0
+    }
+    if (state.input_address.trim().length == 0) {
+        state.error_msg = 'Please enter wallet address'
+        state.is_enter_state = false
+        return
+    }
+    console.log(state.input_address.trim().length)
+    if (state.input_address.trim().length != 42 || state.input_address.trim().substring(0, 2).toLowerCase() != '0x') {
+        state.error_msg = 'Incorrect wallet address input'
+        state.is_enter_state = false
+        return
+    }
+    if (Number(state.input_amount) > Number(state.balance)) {
+        state.error_msg = `Insufficient ${withdraw_info.token_symbol} balance`
+        state.is_enter_state = false
+        state.amount = 0
+        return
+    }
+    if (Number(state.input_amount) < Number(state.min_amount)) {
+        state.error_msg = `Minimum amount: ${state.min_amount} ${withdraw_info.token_symbol}`
+        state.is_enter_state = false
+        state.amount = 0
+        return
+    } else {
+        state.error_msg = ''
+    }
+}
+
+const clickBtn = () => {
+    if (state.is_enter_state) {
+        withdraw_info.data = state
+        // router.push('/withdraw/confirm')
+    }
+}
+
+const initConfig = () => {
+    state.balance = withdraw_info.balance
+    if (withdraw_info.source == 'confirm') {
+        state.withdraw_switch = withdraw_info.data.withdraw_switch
+        state.input_address = withdraw_info.data.input_address
+        state.input_amount = withdraw_info.data.input_amount
+        state.amount = withdraw_info.data.amount
+        state.withdraw_fee_desc = withdraw_info.data.withdraw_fee_desc
+        inputText()
+    } else {
+        getWithdrawConfig({
+            params: {
+                "currencyCode": withdraw_info.currency_code,
+                "withdrawNetwork": withdraw_info.token_chain
+            }
+        }).then((res) => {
+            switch (res.code.toString()) {
+                case '0':
+                    state.withdraw_switch = res.data.withdrawSwitch
+                    // 关闭提现功能
+                    if (!res.data.withdrawSwitch) {
+                        state.error_msg = 'Withdrawal function temporarily closed'
+                        return
+                    }
+
+                    state.fee_amount = res.data.withdrawFee
+                    // 单次提现最小金额
+                    state.min_amount = res.data.withdrawPerMinAmount
+
+                    state.withdraw_fee_desc = res.data.withdrawFeeDesc
+                    break;
+
+                default:
+                    break;
+            }
+        })
+    }
+}
+
+onMounted(() => {
+    initConfig();
+    Report.reportLog({
+        pageSource: Report.pageSource.denetWithdrawForm,
+        businessType: Report.businessType.pageView,
+    });
+    withdraw_info.enter_state = false
+})
+
+const clickWithdrawalAll = () => {
+    state.input_amount = state.balance
+    inputText()
+}
+
+const numToFixed = (str) => {
+    if (String(str).indexOf('.') >= 0) {
+        return Number(str).toFixed(4);
+    } else {
+        return str
+    }
+}
+</script>
+  
+  
+<style lang='scss' scoped>
+.info {
+    position: relative;
+    height: 100%;
+
+    .content {
+        height: calc(100% - 48px - 80px);
+        overflow: auto;
+        padding: 13px 16px 0 13px;
+
+        .area-input-1 {
+
+
+            .token {
+                // padding-right: 7px;
+            }
+
+            .net {
+                // padding-left: 7px;
+                margin-top: 16px;
+            }
+
+            .token,
+            .net {
+
+                .title {
+                    font-weight: 500;
+                    font-size: 12px;
+                    margin-bottom: 6px;
+                    color: #8B8B8B;
+                }
+
+                .box {
+                    border: 1px solid #DBDBDB;
+                    border-radius: 8px;
+                    height: 44px;
+                    display: flex;
+                    align-items: center;
+                    padding: 0 15px 0 10px;
+                    display: flex;
+                    flex-wrap: nowrap;
+                    justify-content: space-between;
+
+                    img {
+                        width: 20px;
+                        height: 20px;
+                    }
+
+                    .up {
+                        width: 13px;
+                        height: 12px;
+                        cursor: pointer;
+                    }
+
+                    span {
+                        flex: 1;
+                        margin-left: 6px;
+                        font-size: 14px;
+                        font-weight: 500;
+                    }
+                }
+            }
+        }
+
+        .area-input-2,
+        .area-input-3 {
+            margin-top: 16px;
+
+            .title {
+                font-weight: 500;
+                font-size: 12px;
+                color: #8B8B8B;
+                margin-bottom: 6px;
+                display: flex;
+                justify-content: space-between;
+            }
+
+            .box {
+                border: 1px solid #DBDBDB;
+                border-radius: 8px;
+                height: 44px;
+                display: flex;
+                align-items: center;
+
+                input {
+                    outline: none;
+                    border: 0;
+                    flex: 1;
+                    height: 18px;
+                    padding: 0 16px;
+                    font-weight: 500;
+                    font-size: 15px;
+
+                    &::placeholder {
+                        color: #B8B8B8;
+                    }
+                }
+
+                input::-webkit-outer-spin-button,
+                input::-webkit-inner-spin-button {
+                    -webkit-appearance: none;
+                }
+
+                input[type="number"] {
+                    -moz-appearance: textfield;
+                }
+
+                span {
+                    display: block;
+                    color: #1D9BF0;
+                    font-size: 13px;
+                    cursor: pointer;
+                    margin-right: 16px;
+                }
+            }
+
+            .error {
+                margin-top: 10px;
+                color: #FFA621;
+                font-weight: 400;
+                font-size: 12px;
+            }
+        }
+
+        .tips {
+            margin-top: 12px;
+            margin-bottom: 12px;
+            font-size: 13px;
+            height: 118;
+            overflow: auto;
+            background: #FCF5E5;
+            display: flex;
+            padding: 10px;
+            border-radius: 10px;
+
+            .tips-icon {
+
+                width: 30px;
+
+                img {
+                    width: 20px;
+                    height: 20px;
+                }
+            }
+
+            .tips-content {
+                flex: 1;
+            }
+
+            p {
+                margin: 0;
+                padding: 0;
+            }
+        }
+
+    }
+
+    .footer {
+        z-index: 11;
+        background: #fff;
+        border-top: 1px solid #DBDBDB;
+        bottom: 0;
+        height: 80px;
+        display: flex;
+        position: absolute;
+        justify-content: space-between;
+        width: 100%;
+        bottom: 0;
+        align-items: center;
+
+        .left {
+            margin-left: 16px;
+
+            .txt {
+                color: #9D9D9D;
+                font-weight: 400;
+                font-size: 12px;
+            }
+
+            .money {
+                color: #000000;
+                font-weight: 500;
+                font-size: 15px;
+            }
+        }
+
+        .right {
+            margin-right: 16px;
+
+            .btn {
+                cursor: pointer;
+                width: 140px;
+                height: 46px;
+                line-height: 46px;
+                text-align: center;
+                font-weight: 600;
+                font-size: 18px;
+                color: #FFFFFF;
+                background: #D2D2D2;
+                border-radius: 100px;
+            }
+
+            .enter {
+                background: #1D9BF0;
+            }
+        }
+    }
+}
+</style>
+  

+ 52 - 0
src/pages/withdraw/paypal.vue

@@ -0,0 +1,52 @@
+<template>
+    <!-- 公共组件 -->
+    <div class="info">
+        <v-head :title="'Withdraw'" :show_more="true"></v-head>
+        <popup-withdraw style="height: calc(100% - 48px);" @back="back"></popup-withdraw>
+    </div>
+</template>
+
+<script setup>
+import VHead from '@/components/v-head.vue'
+import PopupWithdraw from "@/components/popup-withdraw.vue";
+import { useRouter, useRoute } from "vue-router";
+import { provide } from 'vue';
+
+let route = useRoute()
+let router = useRouter()
+let _params = route.query;
+let withdraw_info = {
+    icon_net: require('@/assets/svg/icon-BNB.svg'),
+    chainInfo: JSON.parse(_params.chainInfo),
+    source: 'home',
+    balance: _params.balance,
+    token_symbol: _params.tokenSymbol || '',
+    currency_name: _params.currencyName || '',
+    token_chain: _params.tokenChain || '',
+    currency_code: _params.currencyCode,
+    icon_token: _params.iconPath || '',
+}
+
+// USD
+if (_params.currencyCode == 'USD') {
+    withdraw_info.currency_code = _params.currencyCode
+    withdraw_info.paypal = {
+        amount_value: _params.balance
+    }
+}
+
+provide('withdraw_info', withdraw_info);
+
+const back = () => {
+    router.replace('/')
+}
+
+</script>
+
+
+<style lang='scss' scoped>
+.info {
+    height: 100%;
+    overflow: hidden;
+}
+</style>

+ 15 - 0
src/router/index.js

@@ -47,6 +47,21 @@ const routes = [
         path: '/page-currency-detail',
         name: 'pageCurrencyDetail',
         component: () => require('@/pages/currency/detail')
+    },
+    {
+        path: '/page-withdraw-paypal',
+        name: 'pageWithdrawPaypal',
+        component: () => require('@/pages/withdraw/paypal')
+    },
+    {
+        path: '/page-withdraw-info',
+        name: 'pageWithdrawInfo',
+        component: () => require('@/pages/withdraw/info')
+    },
+    {
+        path: '/page-topup-info',
+        name: 'pageTopupInfo',
+        component: () => require('@/pages/topup/info')
     }
 ]
 

+ 125 - 1
yarn.lock

@@ -2481,6 +2481,15 @@ cli-spinners@^2.5.0:
   resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a"
   integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==
 
+clipboard@^2.0.11:
+  version "2.0.11"
+  resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5"
+  integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==
+  dependencies:
+    good-listener "^1.2.2"
+    select "^1.1.2"
+    tiny-emitter "^2.0.0"
+
 clipboardy@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-2.3.0.tgz#3c2903650c68e46a91b388985bc2774287dba290"
@@ -2490,6 +2499,15 @@ clipboardy@^2.3.0:
     execa "^1.0.0"
     is-wsl "^2.1.1"
 
+cliui@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
+  integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
+  dependencies:
+    string-width "^4.2.0"
+    strip-ansi "^6.0.0"
+    wrap-ansi "^6.2.0"
+
 cliui@^7.0.2, cliui@^7.0.4:
   version "7.0.4"
   resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
@@ -2867,6 +2885,11 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2:
   dependencies:
     ms "2.1.2"
 
+decamelize@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+  integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
+
 deep-is@^0.1.3:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
@@ -2909,6 +2932,11 @@ delayed-stream@~1.0.0:
   resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
   integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
 
+delegate@^3.1.2:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
+  integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
+
 depd@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@@ -2929,6 +2957,11 @@ detect-node@^2.0.4:
   resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
   integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
 
+dijkstrajs@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.2.tgz#2e48c0d3b825462afe75ab4ad5e829c8ece36257"
+  integrity sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==
+
 dir-glob@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@@ -3071,6 +3104,11 @@ emojis-list@^3.0.0:
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
   integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
 
+encode-utf8@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda"
+  integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==
+
 encodeurl@~1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@@ -3584,7 +3622,7 @@ gensync@^1.0.0-beta.2:
   resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
   integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
 
-get-caller-file@^2.0.5:
+get-caller-file@^2.0.1, get-caller-file@^2.0.5:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
   integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
@@ -3670,6 +3708,13 @@ globby@^11.0.2, globby@^11.0.3:
     merge2 "^1.4.1"
     slash "^3.0.0"
 
+good-listener@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+  integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==
+  dependencies:
+    delegate "^3.1.2"
+
 graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
   version "4.2.10"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
@@ -4894,6 +4939,11 @@ pkg-dir@^4.1.0:
   dependencies:
     find-up "^4.0.0"
 
+pngjs@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
+  integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
+
 portfinder@^1.0.26:
   version "1.0.32"
   resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81"
@@ -5249,6 +5299,16 @@ punycode@^2.1.0:
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
   integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
 
+qrcode@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.1.tgz#0103f97317409f7bc91772ef30793a54cd59f0cb"
+  integrity sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg==
+  dependencies:
+    dijkstrajs "^1.0.1"
+    encode-utf8 "^1.0.3"
+    pngjs "^5.0.0"
+    yargs "^15.3.1"
+
 qs@6.10.3:
   version "6.10.3"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
@@ -5417,6 +5477,11 @@ require-from-string@^2.0.2:
   resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
   integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
 
+require-main-filename@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+  integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
 requires-port@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
@@ -5553,6 +5618,11 @@ select-hose@^2.0.0:
   resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
   integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==
 
+select@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+  integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==
+
 selfsigned@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56"
@@ -5631,6 +5701,11 @@ serve-static@1.15.0:
     parseurl "~1.3.3"
     send "0.18.0"
 
+set-blocking@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+  integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
+
 setprototypeof@1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
@@ -6021,6 +6096,11 @@ thunky@^1.0.2:
   resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
   integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
 
+tiny-emitter@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
+  integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
+
 to-fast-properties@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@@ -6422,6 +6502,11 @@ whatwg-url@^5.0.0:
     tr46 "~0.0.3"
     webidl-conversions "^3.0.0"
 
+which-module@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+  integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==
+
 which@^1.2.9:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
@@ -6454,6 +6539,15 @@ wrap-ansi@^3.0.1:
     string-width "^2.1.1"
     strip-ansi "^4.0.0"
 
+wrap-ansi@^6.2.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+  integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
 wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -6485,6 +6579,11 @@ xxhashjs@~0.2.2:
   dependencies:
     cuint "^0.2.2"
 
+y18n@^4.0.0:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
+  integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
+
 y18n@^5.0.5:
   version "5.0.8"
   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
@@ -6505,11 +6604,36 @@ yaml@^1.10.0, yaml@^1.10.2:
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
   integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
 
+yargs-parser@^18.1.2:
+  version "18.1.3"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
+  integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
+  dependencies:
+    camelcase "^5.0.0"
+    decamelize "^1.2.0"
+
 yargs-parser@^20.2.2:
   version "20.2.9"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
   integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
 
+yargs@^15.3.1:
+  version "15.4.1"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
+  integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
+  dependencies:
+    cliui "^6.0.0"
+    decamelize "^1.2.0"
+    find-up "^4.1.0"
+    get-caller-file "^2.0.1"
+    require-directory "^2.1.1"
+    require-main-filename "^2.0.0"
+    set-blocking "^2.0.0"
+    string-width "^4.2.0"
+    which-module "^2.0.0"
+    y18n "^4.0.0"
+    yargs-parser "^18.1.2"
+
 yargs@^16.0.0:
   version "16.2.0"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"