zhb 1 mēnesi atpakaļ
vecāks
revīzija
3e9acb9181

+ 2 - 0
composables/useMenuSplit.ts

@@ -188,6 +188,8 @@ export function useMenuSplit() {
                 { path: '/pages/customer/withdrawal-select', label: 'Home.page_customer.item3', icon: 'icon-withdrawal' },
                 { path: '/pages/customer/payment-history', label: 'Home.page_customer.item4', icon: 'icon-payment' },
                 { path: '/pages/customer/transfer', label: 'Custom.Index.Transfer', icon: 'icon-transfer' },
+                { path: '/pages/customer/wallet-transfer', label: 'wallet.item6', icon: 'icon-transfer' },
+                { path: '/pages/customer/wallet-history', label: 'wallet.item7', icon: 'icon-transfer' },
             ],
         },
         {

+ 14 - 0
pages.json

@@ -205,6 +205,20 @@
         "navigationStyle": "custom"
       }
     },
+    {
+      "path": "pages/customer/wallet-history",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/customer/wallet-transfer",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "custom"
+      }
+    },
     {
       "path": "pages/ib/index",
       "style": {

+ 305 - 0
pages/customer/wallet-history.vue

@@ -0,0 +1,305 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Ib.Report.Tit4')" />
+        <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>
+                    </view>
+                </template>
+                <template #profit="{ row }">
+                    <view class="symbol-cell">
+                        <text :class="getProfitColorClass(row.profit)">{{ row.profit || 0 }}</text>
+                    </view>
+                </template>
+            </cwg-tabel>
+        </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
+})
+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 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 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'
+    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)
+    }
+})
+</script>
+
+<style scoped lang="scss">
+@import "@/uni.scss";
+
+.avatar {
+    width: px2rpx(60);
+    height: px2rpx(60);
+    border-radius: 4px;
+}
+
+.content-title {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-size: px2rpx(20);
+    font-weight: 500;
+
+    .content-title-btns {
+        margin: px2rpx(8) 0;
+
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        gap: px2rpx(12);
+
+        .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);
+        }
+
+        .btn-primary:active {
+            background-color: var(--color-navy-700);
+        }
+    }
+}
+
+.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;
+    }
+}
+
+.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);
+    }
+}
+
+.is-sell,
+.is-loss {
+    color: #eb483f;
+}
+
+.is-buy,
+.is-profit {
+    color: #46cd7c;
+}
+
+
+
+
+.search-bar {
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    flex-wrap: wrap;
+    gap: px2rpx(16);
+    margin: px2rpx(16) 0;
+
+    .cwg-combox,
+    .uni-easyinput,
+    .uni-date {
+        width: px2rpx(240) !important;
+        flex: none;
+    }
+}
+</style>

+ 305 - 0
pages/customer/wallet-transfer.vue

@@ -0,0 +1,305 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Ib.Report.Tit4')" />
+        <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>
+                    </view>
+                </template>
+                <template #profit="{ row }">
+                    <view class="symbol-cell">
+                        <text :class="getProfitColorClass(row.profit)">{{ row.profit || 0 }}</text>
+                    </view>
+                </template>
+            </cwg-tabel>
+        </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
+})
+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 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 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'
+    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)
+    }
+})
+</script>
+
+<style scoped lang="scss">
+@import "@/uni.scss";
+
+.avatar {
+    width: px2rpx(60);
+    height: px2rpx(60);
+    border-radius: 4px;
+}
+
+.content-title {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-size: px2rpx(20);
+    font-weight: 500;
+
+    .content-title-btns {
+        margin: px2rpx(8) 0;
+
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        gap: px2rpx(12);
+
+        .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);
+        }
+
+        .btn-primary:active {
+            background-color: var(--color-navy-700);
+        }
+    }
+}
+
+.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;
+    }
+}
+
+.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);
+    }
+}
+
+.is-sell,
+.is-loss {
+    color: #eb483f;
+}
+
+.is-buy,
+.is-profit {
+    color: #46cd7c;
+}
+
+
+
+
+.search-bar {
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    flex-wrap: wrap;
+    gap: px2rpx(16);
+    margin: px2rpx(16) 0;
+
+    .cwg-combox,
+    .uni-easyinput,
+    .uni-date {
+        width: px2rpx(240) !important;
+        flex: none;
+    }
+}
+</style>