ljc 1 місяць тому
батько
коміт
403cb2b64d
2 змінених файлів з 316 додано та 286 видалено
  1. 107 22
      components/cwg-payment.vue
  2. 209 264
      pages/customer/wallet-transfer.vue

+ 107 - 22
components/cwg-payment.vue

@@ -1,22 +1,25 @@
 <template>
     <view class="notice-container">
-        <cwg-dropdown ref="dropdownRef" :menu-list="customMenuList" @menuClick="handleMenuClick">
-            <view class="pc-header-btn">
+        <cwg-dropdown ref="dropdownRef" :menu-list="[]" @menuClick="handleMenuClick">
+            <view class="pc-header-btn pc-payment-btn">
                 <cwg-icon name="crm-payment" color="#141d22" @click="openNotice" />
-                {{ formattedBalance }} USD
+                <text class="balance-text">{{ formattedBalance }} USD</text>
             </view>
             <template #btn>
-                <view class="right-drawer">
-
-                    <view class="drawer-item">
-                        <view class="drawer-item-title">钱包余额</view>
-                        <view class="drawer-item-content">{{ formattedBalance }} USD</view>
+                <view class="right-drawer custom-payment-drawer">
+                    <view class="drawer-header">
+                        <text class="drawer-title">隐藏余额</text>
+                        <switch :checked="!isShow" @change="toggleShow" color="#6c8595" style="transform:scale(0.7)" />
                     </view>
-                    <view class="drawer-item">
-                        <view class="drawer-item-title">处理中出金金额</view>
-                        <view class="drawer-item-content">{{ formattedPendingWithdrawAmount }} USD</view>
+                    <view class="drawer-content">
+                        <view class="balance-amount">{{ formattedBalance }} USD</view>
+                        <view class="account-type">交易账户</view>
+                        <view class="account-number">#{{ userStore.userInfo.customInfo?.cId || '--' }}</view>
+                    </view>
+                    <view class="drawer-actions">
+                        <button class="action-btn" @click.stop="goPages(1)">转账</button>
+                        <button class="action-btn" @click.stop="goPages(2)">出金</button>
                     </view>
-
                 </view>
             </template>
         </cwg-dropdown>
@@ -29,7 +32,9 @@ import { newsApi } from '@/service/news'
 import useRouter from "@/hooks/useRouter";
 import { drawApi } from "@/service/draw";
 import { useI18n } from 'vue-i18n'
+import useUserStore from '@/stores/use-user-store'
 const { t, locale } = useI18n()
+const userStore = useUserStore()
 import { userToken } from "@/composables/config";
 const isRed = ref(false)
 const dropdownRef = ref(null)
@@ -37,6 +42,7 @@ const close = () => {
     dropdownRef.value.close()
 }
 const router = useRouter();
+const menuList = []
 const customMenuList = computed(() =>
     [{
         label: t('wallet.item6'),
@@ -113,12 +119,16 @@ const getPendingWithdrawAmount = async () => {
         });
     }
 }
+const toggleShow = (e) => {
+    isShow.value = !e.detail.value
+}
+
 const goPages = (type) => {
     let path
     if (type == 1) {
         path = '/pages/customer/wallet-transfer'
     } else if (type == 2) {
-        path = '/pages/customer/wallet-history'
+        path = '/pages/customer/wallet-history' // 此处根据实际“出金”路由修改
     }
     router.push(path)
     close()
@@ -141,14 +151,14 @@ onMounted(() => {
 
 .notice-container {
     :deep(.cwg-dropdown-menu-container) {
-        left: px2rpx(-280) !important;
-        right: px2rpx(0) !important;
+        //left: px2rpx(-280) !important;
+        //right: px2rpx(0) !important;
     }
 
     @media screen and (max-width: 991px) {
         :deep(.cwg-dropdown-menu-container) {
             left: px2rpx(-270) !important;
-            max-width: px2rpx(400);
+            //max-width: px2rpx(400);
         }
     }
 
@@ -174,13 +184,88 @@ onMounted(() => {
         }
     }
 
-    .right-drawer {
-        width: px2rpx(300);
+    .pc-payment-btn {
+        background-color: #e4e9ec;
+        border: 1px solid #141d22;
+        border-radius: px2rpx(4);
+        padding: px2rpx(4) px2rpx(12);
+        height: px2rpx(36);
+        cursor: pointer;
+        
+        .balance-text {
+            margin-left: px2rpx(6);
+            font-size: px2rpx(14);
+            font-weight: 500;
+            color: #141d22;
+        }
+    }
+
+    .custom-payment-drawer {
+        width: px2rpx(260);
         background-color: var(--color-white);
-        display: flex;
-        flex-direction: column;
-        padding: 20px 16px;
-        box-sizing: border-box;
+        padding: 0;
+        border-radius: px2rpx(8);
+        box-shadow: 0 4px 12px rgba(0,0,0,0.1);
+        
+        .drawer-header {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            padding: px2rpx(12) px2rpx(16);
+            border-bottom: 1px solid #f0f0f0;
+            
+            .drawer-title {
+                font-size: px2rpx(14);
+                color: #333;
+            }
+        }
+        
+        .drawer-content {
+            padding: px2rpx(20) px2rpx(16);
+            
+            .balance-amount {
+                font-size: px2rpx(18);
+                font-weight: bold;
+                color: #141d22;
+                margin-bottom: px2rpx(8);
+            }
+            
+            .account-type {
+                font-size: px2rpx(13);
+                color: #666;
+                margin-bottom: px2rpx(4);
+            }
+            
+            .account-number {
+                font-size: px2rpx(13);
+                color: #999;
+            }
+        }
+        
+        .drawer-actions {
+            display: flex;
+            gap: px2rpx(12);
+            padding: 0 px2rpx(16) px2rpx(20);
+            
+            .action-btn {
+                flex: 1;
+                height: px2rpx(36);
+                line-height: px2rpx(36);
+                background-color: #f5f7fa;
+                color: #333;
+                font-size: px2rpx(13);
+                border-radius: px2rpx(4);
+                margin: 0;
+                
+                &::after {
+                    border: none;
+                }
+                
+                &:active {
+                    background-color: #e4e7ed;
+                }
+            }
+        }
     }
 
     .notification-list {

+ 209 - 264
pages/customer/wallet-transfer.vue

@@ -1,305 +1,250 @@
 <template>
     <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
         <cwg-header :title="t('wallet.item62')" />
-        <view class="info-card">
-            <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
-                @reset="handleReset" />
-            <cwg-tabel ref="tableRef" :columns="columns" :immediate="false" :mobilePrimaryFields="mobilePrimaryFields"
-                :queryParams="search" :api="listApi" :show-operation="false">
-                <template #symbol="{ row }">
-                    <view class="symbol-cell">
-                        <view class="pair">{{ getSymbolParts(row.symbol)[0] }}/{{ getSymbolParts(row.symbol)[1]
-                        }}</view>
-                        <view class="desc">{{ row.openPrice }}
-                            <text :class="getCmdColorClass(row.cmdName)">{{ formatCmdName(row.cmdName) }}{{ row.volume
-                            }}{{ t('Label.Lot') }}</text>
+        <view id="custom_WalletTransfer" class="transfer-page">
+            <view class="main-content">
+                <view class="box box-step2">
+                    <view class="b-card">
+                        <view class="card-top">
+                            <uni-forms ref="formRef" :model="form" :rules="rules" label-position="top"
+                                validate-trigger="submit">
+                                
+                                <view class="card-row card-tit">
+                                    <view class="title-wrapper">
+                                        <view class="tit">
+                                            <text class="iconfont icon-caret-right"></text>
+                                            <span>{{ t('wallet.item63') }}</span>
+                                        </view>
+                                    </view>
+                                </view>
+                                <view class="card-row">
+                                    <uni-forms-item name="walletbalance">
+                                        <uni-easyinput v-model="walletbalanceDisplay" disabled class="disabled-input" />
+                                    </uni-forms-item>
+                                </view>
+
+                                <view class="card-row card-tit">
+                                    <view class="title-wrapper">
+                                        <view class="tit">
+                                            <text class="iconfont icon-caret-right"></text>
+                                            <span>{{ t('Custom.Transfer.IntoAccount') }}</span>
+                                        </view>
+                                    </view>
+                                </view>
+                                <view class="card-row">
+                                    <uni-forms-item name="login">
+                                        <cwg-combox v-model:value="form.login" :clearable="false"
+                                            :options="toOptionsDisplay" :placeholder="t('placeholder.choose')" />
+                                    </uni-forms-item>
+                                </view>
+
+                                <view class="card-row card-tit">
+                                    <view class="title-wrapper">
+                                        <view class="tit">
+                                            <text class="iconfont icon-caret-right"></text>
+                                            <span>{{ t('Label.Currency') }}</span>
+                                        </view>
+                                    </view>
+                                </view>
+                                <view class="card-row">
+                                    <uni-forms-item name="currency">
+                                        <cwg-combox v-model:value="form.currency" :clearable="false"
+                                            :options="currencyOptions" :placeholder="t('placeholder.choose')" />
+                                    </uni-forms-item>
+                                </view>
+
+                                <view class="card-row card-tit">
+                                    <view class="title-wrapper">
+                                        <view class="tit">
+                                            <text class="iconfont icon-caret-right"></text>
+                                            <span>{{ t('Label.Amount') }}</span>
+                                        </view>
+                                    </view>
+                                </view>
+                                <view class="card-row">
+                                    <uni-forms-item name="amount">
+                                        <uni-easyinput v-model="form.amount" :placeholder="t('placeholder.input')" />
+                                    </uni-forms-item>
+                                </view>
+
+                                <view class="form-row">
+                                    <button class="s-btn reselect" type="primary" @click="toTransfer">{{ t('Btn.Submit') }}</button>
+                                </view>
+                            </uni-forms>
                         </view>
                     </view>
-                </template>
-                <template #profit="{ row }">
-                    <view class="symbol-cell">
-                        <text :class="getProfitColorClass(row.profit)">{{ row.profit || 0 }}</text>
-                    </view>
-                </template>
-            </cwg-tabel>
+                </view>
+            </view>
+            <!-- 失败弹窗 -->
+            <cwg-error-popup v-model:visible="dialogError" @confirm="closeDia" :responseMessage="RES" />
+            <!-- 成功弹窗 -->
+            <cwg-success-popup v-model:visible="dialogSuccess" @confirm="closeDia" />
+            <!-- 等待弹窗 -->
+            <cwg-wait-popup v-model:visible="dialogCheckWait" type="center" :mask-click="false" :showFooters="false" />
         </view>
     </cwg-page-wrapper>
 </template>
 
 <script setup lang="ts">
-import { computed, ref, nextTick, reactive } from 'vue';
-import { useI18n } from 'vue-i18n';
-const { t, locale } = useI18n();
-import { onLoad } from '@dcloudio/uni-app'
-import { customApi } from '@/service/custom';
-import { useAccountOptions } from '@/composables/useAccountOptions'
-const { loginOptions, isLoaded, isSuccess } = useAccountOptions()
-const search = reactive({
-    login: null
+import { ref, reactive, computed, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { customApi } from '@/service/custom'
+import { drawApi } from '@/service/draw'
+import Config from '@/config/index'
+
+const { Code } = Config
+const { t } = useI18n()
+
+const loading = ref(false)
+const flag = ref(false)
+const RES = ref('')
+const walletbalance = ref('0')
+const toOptions = ref([])
+
+const form = reactive({
+    currency: 'USD',
+    login: null,
+    amount: ''
 })
-const typeMap = computed(() => ([
-    { value: null, text: t('Custom.PaymentHistory.All') },
-    { value: 1, text: t('Custom.PaymentHistory.Deposit') },
-    { value: 2, text: t('Custom.PaymentHistory.Withdrawals') }
-]));
-const orderStatusMap = computed(() => ([
-    { value: null, text: t('Custom.PaymentHistory.All') },
-    { value: 1, text: t('State.ToBeProcessed') },
-    { value: 2, text: t('State.Completed') },
-    { value: 3, text: t('State.InTheProcessing') },
-    { value: 4, text: t('State.Refused') },
-    { value: 5, text: t('State.expireTime') },
-    { value: 6, text: t('State.Cancelled') },
-]));
-
-// 表格列配置
-const columns = computed(() => [
-    {
-        prop: 'symbol',
-        label: t('Label.Varieties'),      // 交易品种
-        align: 'left'
-    },
-    {
-        prop: 'cmdName',
-        label: t('Label.Type'),           // 类型
-        align: 'left'
-    },
-    {
-        prop: 'openTime',
-        label: t('Label.OpenTime'),       // 开仓时间(协调世界时)
-        align: 'left'
-    },
-    {
-        prop: 'volume',
-        label: t('Label.Volume'),         // 手
-        formatter: ({ row }) => `${row.volume || 0} ${t('Label.Lot')}`,
-        align: 'right'
-    },
-    {
-        prop: 'openPrice',
-        label: t('Label.OpenPrice'),      // 开仓价
-        align: 'right'
-    },
-    {
-        prop: 'tp',
-        label: t('Label.EP'), // 止盈
-        align: 'right'
-    },
-    {
-        prop: 'sl',
-        label: t('Label.EL'), // 止损
-        align: 'right'
-    },
-    {
-        prop: 'profit',
-        label: t('Label.ProfitLoss') + '(USD)',     // 利润, USD
-        slot: 'profit',
-        align: 'right'
-    },
-    {
-        prop: 'comment',
-        label: t('Label.Note'),
-        align: 'right',
-        isTabel: false
-    },
-    {
-        prop: 'more',
-        type: 'more',
-        width: 20,
-        align: 'right'
-    },
-])
-
-const mobilePrimaryFields = computed(() => [
-    {
-        prop: 'symbol',
-        label: t('Label.Varieties'),      // 交易品种
-        align: 'left',
-        slot: 'symbol'
-    },
-    {
-        prop: 'profit',
-        label: t('Label.ProfitLoss') + '(USD)',     // 利润, USD
-        slot: 'profit',
-        align: 'right'
-    },
-    {
-        prop: 'more',
-        type: 'more',
-        width: 20,
-        align: 'right'
-    },
-])
 
-// 动态传入筛选字段配置
-const filterFields = computed(() => [
-    isLoaded.value && isSuccess.value && { key: 'login', type: 'select', label: t('Custom.PaymentHistory.TradingAccount'), placeholder: t('placeholder.login'), options: loginOptions || [], defaultValue: search.login || undefined },
-    { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
-])
-const searchParams = ref({})
-const tableRef = ref(null)
-const handleSearch = (params) => {
-    Object.assign(search, params)
-    search.login = Number(params.login)
-    search.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
-    if(!search.platform) return
-    nextTick(() => {
-        tableRef.value.refreshTable()
-    })
-}
+const currencyOptions = [{ text: 'USD', value: 'USD' }]
 
-const handleReset = (params) => {
-    Object.assign(search, params)
-    search.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
-    if(!search.platform) return
-    nextTick(() => {
-        tableRef.value.refreshTable()
-    })
-}
-
-const listApi = ref(null)
-listApi.value = customApi.tradePosition
+const walletbalanceDisplay = computed(() => `$ ${walletbalance.value}`)
 
-const getSymbolParts = (sym: string) => {
-    if (!sym) return ['', '']
-    const s = String(sym).toUpperCase()
-    if (s.includes('/')) {
-        const [base, quote] = s.split('/')
-        return [base, quote]
-    }
-    const base = s.slice(0, 3)
-    const quote = s.slice(3)
-    return [base, quote]
-}
-const formatCmdName = (cmd: string) => {
-    const v = String(cmd || '').toLowerCase()
-    if (v.includes('sell')) return '卖出'
-    if (v.includes('buy')) return '买入'
-    return cmd || ''
-}
-const getCmdColorClass = (cmd: string) => {
-    const v = String(cmd || '').toLowerCase()
-    if (v.includes('sell')) return 'is-sell'
-    if (v.includes('buy')) return 'is-buy'
+const groupTypeName = (type) => {
+    if (type == '1') return t("AccountType.ClassicAccount")
+    if (type == '2') return t("AccountType.SeniorAccount")
+    if (type == '5') return t("AccountType.SpeedAccount")
+    if (type == '6') return t("AccountType.SpeedAccount")
+    if (type == '7') return t("AccountType.StandardAccount")
+    if (type == '8') return t("AccountType.CentAccount")
     return ''
 }
-const getProfitColorClass = (profit: any) => {
-    const n = Number(profit)
-    if (!Number.isFinite(n) || n === 0) return ''
-    return n > 0 ? 'is-profit' : 'is-loss'
-}
 
-onLoad((e) => {
-    if (e.login) {
-        search.login = Number(e.login)
-    }
+const toOptionsDisplay = computed(() => {
+    return toOptions.value.map(item => ({
+        text: `${item.login} - ${item.platform} - ${groupTypeName(item.type)}`,
+        value: item.login
+    }))
 })
-</script>
 
-<style scoped lang="scss">
-@import "@/uni.scss";
+const dialogCheck = ref(false)
+const dialogVisible = ref(false)
+const dialogCheckWait = ref(false)
 
-.avatar {
-    width: px2rpx(60);
-    height: px2rpx(60);
-    border-radius: 4px;
-}
+const dialogSuccess = computed(() => dialogCheck.value && dialogVisible.value)
+const dialogError = computed(() => dialogCheck.value && !dialogVisible.value)
 
-.content-title {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    font-size: px2rpx(20);
-    font-weight: 500;
+const rules = {
+    login: {
+        rules: [{ required: true, errorMessage: t('vaildate.select.empty') }]
+    },
+    currency: {
+        rules: [{ required: true, errorMessage: t('vaildate.select.empty') }]
+    },
+    amount: {
+        rules: [
+            { required: true, errorMessage: t('vaildate.amount.format') },
+            {
+                validateFunction: (rule, value, data, callback) => {
+                    if (!value) {
+                        callback(t('vaildate.amount.format'))
+                    } else if (!/^[0-9]+([.]{1}[0-9]{1,2})?$/.test(value)) {
+                        callback(t('vaildate.amount.format'))
+                    }
+                    return true
+                }
+            }
+        ]
+    }
+}
 
-    .content-title-btns {
-        margin: px2rpx(8) 0;
+const formRef = ref(null)
 
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        gap: px2rpx(12);
+const closeDia = () => {
+    if (formRef.value) {
+        form.amount = ''
+        form.login = null
+        formRef.value.clearValidate()
+    }
+    dialogCheck.value = false
+    dialogVisible.value = false
+}
 
-        .btn-primary {
-            min-width: px2rpx(120);
-            background-color: var(--color-error);
-            color: white;
-            padding: 0 px2rpx(12);
-            border: none;
-            font-size: px2rpx(14);
-            text-align: center;
-            cursor: pointer;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            gap: px2rpx(8);
+const toTransfer = async () => {
+    try {
+        await formRef.value.validate()
+        
+        if (walletbalance.value == '0' || Number(walletbalance.value) < Number(form.amount)) {
+            uni.showToast({ title: t('wallet.item64'), icon: 'none' })
+            return
         }
 
-        .btn-primary:active {
-            background-color: var(--color-navy-700);
+        if (flag.value) return
+        flag.value = true
+        dialogCheckWait.value = true
+
+        let res = await customApi.walletTransferApply({ ...form })
+        
+        if (res.code == Code.StatusOK) {
+            dialogCheck.value = true
+            dialogVisible.value = true
+            flag.value = false
+            getWalletList() // refresh balance after success
+        } else {
+            RES.value = res.msg
+            dialogCheck.value = true
+            dialogVisible.value = false
+            flag.value = false
         }
+        dialogCheckWait.value = false
+    } catch (e) {
+        console.log(e)
     }
 }
 
-.operation-btn {
-    :deep(span) {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        gap: px2rpx(4);
-        cursor: pointer;
-        background-color: var(--color-slate-150);
-        padding: px2rpx(8) 0;
+const getToDateList = async () => {
+    try {
+        let res = await customApi.CustomDropdown({})
+        if (res.code == Code.StatusOK) {
+            toOptions.value = res.data || []
+        } else {
+            uni.showToast({ title: res.msg, icon: 'none' })
+        }
+    } catch (e) {
+        console.log(e)
     }
 }
 
-.operation-btn.disabled {
-    cursor: not-allowed;
-    opacity: 0.5;
-}
-
-.symbol-cell {
-    display: inline-flex;
-    align-items: flex-start;
-    gap: 0.25rem;
-    flex-direction: column;
-
-    .pair {
-        font-weight: 600;
-        color: var(--color-slate-900);
-    }
-
-    .desc {
-        color: var(--color-slate-600);
+const getWalletList = async () => {
+    try {
+        let res = await drawApi.walletbalance({})
+        if (res.code == Code.StatusOK) {
+            walletbalance.value = res.data != null ? res.data : '0'
+        } else {
+            uni.showToast({ title: res.msg, icon: 'none' })
+        }
+    } catch (e) {
+        console.log(e)
     }
 }
 
-.is-sell,
-.is-loss {
-    color: #eb483f;
-}
-
-.is-buy,
-.is-profit {
-    color: #46cd7c;
-}
-
-
-
+onMounted(() => {
+    getToDateList()
+    getWalletList()
+})
+</script>
 
-.search-bar {
-    display: flex;
-    align-items: center;
-    justify-content: flex-start;
-    flex-wrap: wrap;
-    gap: px2rpx(16);
-    margin: px2rpx(16) 0;
+<style scoped lang="scss">
+@import "@/uni.scss";
+@import "@/pages/customer/transfer.scss";
 
-    .cwg-combox,
-    .uni-easyinput,
-    .uni-date {
-        width: px2rpx(240) !important;
-        flex: none;
+:deep(.disabled-input) {
+    .uni-easyinput__content {
+        background-color: #f5f7fa !important;
+        input {
+            color: #c0c4cc !important;
+        }
     }
 }
 </style>