zhb 1 месяц назад
Родитель
Сommit
8ac0b1fbf8

+ 7 - 3
components/cwg-sidebar.vue

@@ -61,13 +61,17 @@
             </view>
         </view>
         <view class="menu fixed">
+            <view class="menu-item ib-box" @click="setMode('customer')" v-if="mode !== 'customer'">
+                <cwg-icon name="crm-trade" :size="20" color="#6c8595" />
+                <view class="menu-label" v-t="'Home.msg.Custom'" />
+            </view>
             <view class="menu-item ib-box" @click="setMode('ib')" v-if="mode !== 'ib' && ibStatus">
                 <cwg-icon name="crm-ib" :size="20" color="#6c8595" />
                 <view class="menu-label" v-t="'Home.msg.Ib'" />
             </view>
-            <view class="menu-item ib-box" @click="setMode('customer')" v-if="mode !== 'customer'">
-                <cwg-icon name="crm-trade" :size="20" color="#6c8595" />
-                <view class="menu-label" v-t="'Home.msg.Custom'" />
+            <view class="menu-item ib-box" @click="setMode('follow')" v-if="mode !== 'follow'">
+                <cwg-icon name="crm-gd" :size="20" color="#6c8595" />
+                <view class="menu-label" v-t="'Documentary.title'" />
             </view>
         </view>
     </view>

+ 74 - 3
composables/useMenuSplit.ts

@@ -47,8 +47,19 @@ export function useMenuSplit(handleClick1: (item: MenuItem) => void) {
     // 设置全局模式
     function setMode(code: string) {
         globalStore.setMode(code);
-        const homePath = mode.value === 'customer' ? '/pages/customer/index' : '/pages/ib/index'
-        router.reLaunch(homePath)
+        switch (code) {
+            case 'follow':
+                router.reLaunch('/pages/follow/index')
+                break
+            case 'ib':
+                router.reLaunch('/pages/ib/index')
+                break
+            case 'customer':
+                router.reLaunch('/pages/customer/index')
+                break
+            default:
+                break
+        }
         if (code === 'ib') {
             uni.$emit('open-ib')
         }
@@ -294,12 +305,72 @@ export function useMenuSplit(handleClick1: (item: MenuItem) => void) {
             ],
         },
     ])
+    const followBaseMenus = computed<MenuItem[]>(() => [
+        {
+            isOpenMenu: false,
+            path: '/pages/follow/index',
+            label: 'Documentary.console.item1',
+            icon: 'crm-mb',
+        },
+        {
+            isOpenMenu: false,
+            path: '/pages/follow/trading-center',
+            label: 'Documentary.page_doc.item2',
+            icon: 'crm-gd',
+        },
+        {
+            isOpenMenu: false,
+            submenuHeight: 0,
+            path: '/',
+            label: 'Documentary.page_doc.item3',
+            icon: 'crm-newspaper',
+            children: [
+                { path: '/pages/follow/report', label: 'Custom.Index.AccountList', icon: 'icon-client' },
+            ],
+        },
+        {
+            isOpenMenu: false,
+            submenuHeight: 0,
+            path: '/',
+            label: 'Documentary.page_doc.item4',
+            icon: 'crm-payment',
+            children: [
+                { path: '/pages/follow/transfer', label: 'Documentary.TundManagement.item2', icon: 'icon-client' },
+                { path: '/pages/follow/transfer-history', label: 'Documentary.TundManagement.item3', icon: 'icon-transfer' }
+            ],
+        },
+        {
+            isOpenMenu: false,
+            submenuHeight: 0,
+            path: '/',
+            label: 'Documentary.page_doc.item5',
+            icon: 'crm-trade',
+            children: [
+                { path: '/pages/follow/trading-management', label: 'Documentary.TundManagement.item8', icon: 'icon-client' },
+                { path: '/pages/follow/account-management', label: 'Documentary.TundManagement.item9', icon: 'icon-transfer' },
+                { path: '/pages/follow/record', label: 'Documentary.TundManagement.item10', icon: 'icon-transfer' }
+            ],
+        },
+    ])
     const menus = ref<MenuItem[]>([])
 
     // 监听 mode 变化,自动导航到对应首页
     watch(mode, (newMode, oldMode) => {
         if (newMode !== oldMode) {
-            const base = newMode === 'customer' ? [...customerBaseMenus.value] : [...ibBaseMenus.value]
+            let base: MenuItem[] = []
+            switch (newMode) {
+                case 'follow':
+                    base = [...followBaseMenus.value]
+                    break
+                case 'ib':
+                    base = [...ibBaseMenus.value]
+                    break
+                case 'customer':
+                    base = [...customerBaseMenus.value]
+                    break
+                default:
+                    break
+            }
             if (shouldShowLanguageMenu.value) {
                 base.push(languageMenuItem.value)
             }

+ 49 - 0
pages.json

@@ -386,6 +386,55 @@
         "navigationBarTitleText": "",
         "navigationStyle": "follow"
       }
+    },
+    {
+      "path": "pages/follow/report",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "follow"
+      }
+    },
+    {
+      "path": "pages/follow/transfer",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "follow"
+      }
+    },
+    {
+      "path": "pages/follow/transfer-history",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "follow"
+      }
+    },
+    {
+      "path": "pages/follow/trading-management",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "follow"
+      }
+    },
+    {
+      "path": "pages/follow/account-management",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "follow"
+      }
+    },
+    {
+      "path": "pages/follow/record",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "follow"
+      }
+    },
+    {
+      "path": "pages/follow/trading-center",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "follow"
+      }
     }
   ],
   "tabBar": {

+ 403 - 0
pages/follow/account-management.vue

@@ -0,0 +1,403 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Documentary.TundManagement.item9')" />
+        <view class="info-card">
+            <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
+                @reset="handleReset" />
+            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="search" :api="listApi"
+                :show-operation="false">
+                <!-- 状态列自定义渲染 -->
+                <template #status="{ row }">
+                    <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">
+                        {{ getStatusText(row) }}
+                    </view>
+                    <view v-else></view>
+                </template>
+                <!-- 账户类型列自定义渲染 -->
+                <template #accountType="{ row }">
+                    {{ getAccountTypeText(row.type || row.loginType) }}
+                </template>
+                <!-- 金额列格式化 -->
+                <template #amount="{ row }">
+                    <view>-{{ formatNumber(row.withdrawAmount || row.amount) }}</view>
+                </template>
+                <!-- 备注列格式化 -->
+                <template #note="{ row }">
+                    <view>{{ formatNote(row.approveDesc) }}</view>
+                </template>
+            </cwg-tabel>
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+const { t, locale } = useI18n();
+import { customApi } from '@/service/custom';
+import useUserStore from "@/stores/use-user-store";
+const userStore = useUserStore();
+const userInfo = computed(() => userStore.userInfo);
+const search = ref({
+    type: 1
+})
+const getInfoAgentTransfer = computed(() => userInfo.value.customInfo?.agentTransfer)
+const typeMap = computed(() => ([
+    { value: 1, text: t('Custom.Recording.NewAccount') },
+    { value: 2, text: t('Custom.Recording.LeverageApply') },
+    { value: 3, text: t('Custom.Recording.InternalTransfer') },
+    { value: 4, text: t('Custom.Recording.ActivitiesApply') },
+    ...(getInfoAgentTransfer.value == 1 ? [{ value: 5, text: t('Home.page_ib.item9') }] : [])
+]));
+const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
+
+// 账户类型映射
+const accountTypeMap = {
+    1: 'AccountType.ClassicAccount',
+    2: 'AccountType.SeniorAccount',
+    5: 'AccountType.SpeedAccount',
+    6: 'AccountType.SpeedAccount',
+    7: 'AccountType.StandardAccount',
+    8: 'AccountType.CentAccount'
+}
+// 拒绝原因映射(示例)
+const reasons = ref({})
+// 根据类型获取列配置
+const getColumnsByType = (type: number) => {
+    switch (type) {
+        case 1: // 开户记录
+            return [
+                {
+                    prop: 'platform',
+                    label: t('Custom.Recording.Platform'),
+                    align: 'left'
+                },
+                {
+                    prop: 'type',
+                    label: t('Custom.PaymentHistory.payType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.currency || '--',
+                },
+                {
+                    prop: 'leverage',
+                    label: t('Custom.Recording.Lever'),
+                    formatter: ({ row }) => `1: ${row.leverage}` || '--',
+                    align: 'left'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.Recording.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 2: // 杠杆修改记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'oldLeverage',
+                    label: t('Custom.Recording.OldLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.oldLeverage ? `1:${row.oldLeverage}` : '--'
+                },
+                {
+                    prop: 'newLeverage',
+                    label: t('Custom.Recording.NewLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.newLeverage ? `1:${row.newLeverage}` : '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 3: // 转账记录
+        case 5: // 内部转账记录
+            return [
+                {
+                    prop: 'withdrawLogin',
+                    label: t('Custom.Recording.TransferAccounts'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawLogin || '--'
+                },
+                {
+                    prop: 'depositLogin',
+                    label: t('Custom.Recording.IntoAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.depositLogin || '--',
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawCurrency || '--',
+                },
+                {
+                    prop: 'withdrawAmount',
+                    label: t('Custom.Recording.Amount'),
+                    align: 'left',
+                    slot: 'amount'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 4: // 活动申请记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'loginType',
+                    label: t('Custom.Recording.AccountType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'title',
+                    label: t('Custom.Recording.ActivityName'),
+                    align: 'left',
+                    formatter: ({ row }) => row.title || '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+    }
+}
+// 动态传入筛选字段配置
+const filterFields = computed(() => [
+    { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: 1 },
+    { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
+])
+const searchParams = ref({})
+const tableRef = ref(null)
+const handleSearch = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+
+const handleReset = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+// 当前列配置
+const currentColumns = computed(() => getColumnsByType(search.value.type))
+// 获取状态文本
+const getStatusText = (row: any) => {
+    const status = row.status
+    // 根据不同记录类型处理状态
+    if (search.value.type === 1) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.accountStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.accountStatus === 1 || !row.accountStatus)) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 2) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.leverageStatus === 2) return t('State.Completed')
+        if (status === 2 && row.leverageStatus === 1) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 3 || search.value.type === 5) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.withdrawStatus === 2 && row.depositStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.withdrawStatus === 1 || row.depositStatus === 1)) return t('State.InTheProcessing')
+        if (status === 3 || row.withdrawStatus === 3 || row.depositStatus === 3) return t('State.Refused')
+    } else {
+        // 活动申请等
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2) return t('State.Completed')
+        if (status === 3) return t('State.Refused')
+    }
+    return ''
+}
+// 获取状态样式类
+const getStatusClass = (status: number) => {
+    const classMap: Record<number, string> = {
+        1: 'status-pending',
+        2: 'status-success',
+        3: 'status-processing',
+        4: 'status-danger'
+    }
+    return classMap[status] || ''
+}
+// 获取账户类型文本
+const getAccountTypeText = (type: number) => {
+    const key = accountTypeMap[type as keyof typeof accountTypeMap]
+    return key ? t(key) : '--'
+}
+// 格式化数字
+const formatNumber = (value: string | number) => {
+    if (!value) return '--'
+    const num = Number(value)
+    return isNaN(num) ? '--' : num.toFixed(2)
+}
+// 格式化备注
+const formatNote = (approveDesc: string) => {
+    if (!approveDesc) return '--'
+    const reason = reasons.value[approveDesc as keyof typeof reasons.value]
+    if (reason) {
+        return isZh.value ? reason.content : reason.enContent
+    }
+    return approveDesc
+}
+const listApi = ref(null)
+listApi.value = customApi.CustomRecordAccount
+</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: #cf1322;
+            ;
+        }
+    }
+}
+
+.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;
+}
+
+.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>

+ 403 - 0
pages/follow/record.vue

@@ -0,0 +1,403 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Documentary.TundManagement.item10')" />
+        <view class="info-card">
+            <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
+                @reset="handleReset" />
+            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="search" :api="listApi"
+                :show-operation="false">
+                <!-- 状态列自定义渲染 -->
+                <template #status="{ row }">
+                    <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">
+                        {{ getStatusText(row) }}
+                    </view>
+                    <view v-else></view>
+                </template>
+                <!-- 账户类型列自定义渲染 -->
+                <template #accountType="{ row }">
+                    {{ getAccountTypeText(row.type || row.loginType) }}
+                </template>
+                <!-- 金额列格式化 -->
+                <template #amount="{ row }">
+                    <view>-{{ formatNumber(row.withdrawAmount || row.amount) }}</view>
+                </template>
+                <!-- 备注列格式化 -->
+                <template #note="{ row }">
+                    <view>{{ formatNote(row.approveDesc) }}</view>
+                </template>
+            </cwg-tabel>
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+const { t, locale } = useI18n();
+import { customApi } from '@/service/custom';
+import useUserStore from "@/stores/use-user-store";
+const userStore = useUserStore();
+const userInfo = computed(() => userStore.userInfo);
+const search = ref({
+    type: 1
+})
+const getInfoAgentTransfer = computed(() => userInfo.value.customInfo?.agentTransfer)
+const typeMap = computed(() => ([
+    { value: 1, text: t('Custom.Recording.NewAccount') },
+    { value: 2, text: t('Custom.Recording.LeverageApply') },
+    { value: 3, text: t('Custom.Recording.InternalTransfer') },
+    { value: 4, text: t('Custom.Recording.ActivitiesApply') },
+    ...(getInfoAgentTransfer.value == 1 ? [{ value: 5, text: t('Home.page_ib.item9') }] : [])
+]));
+const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
+
+// 账户类型映射
+const accountTypeMap = {
+    1: 'AccountType.ClassicAccount',
+    2: 'AccountType.SeniorAccount',
+    5: 'AccountType.SpeedAccount',
+    6: 'AccountType.SpeedAccount',
+    7: 'AccountType.StandardAccount',
+    8: 'AccountType.CentAccount'
+}
+// 拒绝原因映射(示例)
+const reasons = ref({})
+// 根据类型获取列配置
+const getColumnsByType = (type: number) => {
+    switch (type) {
+        case 1: // 开户记录
+            return [
+                {
+                    prop: 'platform',
+                    label: t('Custom.Recording.Platform'),
+                    align: 'left'
+                },
+                {
+                    prop: 'type',
+                    label: t('Custom.PaymentHistory.payType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.currency || '--',
+                },
+                {
+                    prop: 'leverage',
+                    label: t('Custom.Recording.Lever'),
+                    formatter: ({ row }) => `1: ${row.leverage}` || '--',
+                    align: 'left'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.Recording.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 2: // 杠杆修改记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'oldLeverage',
+                    label: t('Custom.Recording.OldLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.oldLeverage ? `1:${row.oldLeverage}` : '--'
+                },
+                {
+                    prop: 'newLeverage',
+                    label: t('Custom.Recording.NewLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.newLeverage ? `1:${row.newLeverage}` : '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 3: // 转账记录
+        case 5: // 内部转账记录
+            return [
+                {
+                    prop: 'withdrawLogin',
+                    label: t('Custom.Recording.TransferAccounts'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawLogin || '--'
+                },
+                {
+                    prop: 'depositLogin',
+                    label: t('Custom.Recording.IntoAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.depositLogin || '--',
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawCurrency || '--',
+                },
+                {
+                    prop: 'withdrawAmount',
+                    label: t('Custom.Recording.Amount'),
+                    align: 'left',
+                    slot: 'amount'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 4: // 活动申请记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'loginType',
+                    label: t('Custom.Recording.AccountType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'title',
+                    label: t('Custom.Recording.ActivityName'),
+                    align: 'left',
+                    formatter: ({ row }) => row.title || '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+    }
+}
+// 动态传入筛选字段配置
+const filterFields = computed(() => [
+    { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: 1 },
+    { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
+])
+const searchParams = ref({})
+const tableRef = ref(null)
+const handleSearch = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+
+const handleReset = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+// 当前列配置
+const currentColumns = computed(() => getColumnsByType(search.value.type))
+// 获取状态文本
+const getStatusText = (row: any) => {
+    const status = row.status
+    // 根据不同记录类型处理状态
+    if (search.value.type === 1) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.accountStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.accountStatus === 1 || !row.accountStatus)) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 2) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.leverageStatus === 2) return t('State.Completed')
+        if (status === 2 && row.leverageStatus === 1) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 3 || search.value.type === 5) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.withdrawStatus === 2 && row.depositStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.withdrawStatus === 1 || row.depositStatus === 1)) return t('State.InTheProcessing')
+        if (status === 3 || row.withdrawStatus === 3 || row.depositStatus === 3) return t('State.Refused')
+    } else {
+        // 活动申请等
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2) return t('State.Completed')
+        if (status === 3) return t('State.Refused')
+    }
+    return ''
+}
+// 获取状态样式类
+const getStatusClass = (status: number) => {
+    const classMap: Record<number, string> = {
+        1: 'status-pending',
+        2: 'status-success',
+        3: 'status-processing',
+        4: 'status-danger'
+    }
+    return classMap[status] || ''
+}
+// 获取账户类型文本
+const getAccountTypeText = (type: number) => {
+    const key = accountTypeMap[type as keyof typeof accountTypeMap]
+    return key ? t(key) : '--'
+}
+// 格式化数字
+const formatNumber = (value: string | number) => {
+    if (!value) return '--'
+    const num = Number(value)
+    return isNaN(num) ? '--' : num.toFixed(2)
+}
+// 格式化备注
+const formatNote = (approveDesc: string) => {
+    if (!approveDesc) return '--'
+    const reason = reasons.value[approveDesc as keyof typeof reasons.value]
+    if (reason) {
+        return isZh.value ? reason.content : reason.enContent
+    }
+    return approveDesc
+}
+const listApi = ref(null)
+listApi.value = customApi.CustomRecordAccount
+</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: #cf1322;
+            ;
+        }
+    }
+}
+
+.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;
+}
+
+.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>

+ 403 - 0
pages/follow/report.vue

@@ -0,0 +1,403 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Documentary.TundManagement.item10')" />
+        <view class="info-card">
+            <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
+                @reset="handleReset" />
+            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="search" :api="listApi"
+                :show-operation="false">
+                <!-- 状态列自定义渲染 -->
+                <template #status="{ row }">
+                    <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">
+                        {{ getStatusText(row) }}
+                    </view>
+                    <view v-else></view>
+                </template>
+                <!-- 账户类型列自定义渲染 -->
+                <template #accountType="{ row }">
+                    {{ getAccountTypeText(row.type || row.loginType) }}
+                </template>
+                <!-- 金额列格式化 -->
+                <template #amount="{ row }">
+                    <view>-{{ formatNumber(row.withdrawAmount || row.amount) }}</view>
+                </template>
+                <!-- 备注列格式化 -->
+                <template #note="{ row }">
+                    <view>{{ formatNote(row.approveDesc) }}</view>
+                </template>
+            </cwg-tabel>
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+const { t, locale } = useI18n();
+import { customApi } from '@/service/custom';
+import useUserStore from "@/stores/use-user-store";
+const userStore = useUserStore();
+const userInfo = computed(() => userStore.userInfo);
+const search = ref({
+    type: 1
+})
+const getInfoAgentTransfer = computed(() => userInfo.value.customInfo?.agentTransfer)
+const typeMap = computed(() => ([
+    { value: 1, text: t('Custom.Recording.NewAccount') },
+    { value: 2, text: t('Custom.Recording.LeverageApply') },
+    { value: 3, text: t('Custom.Recording.InternalTransfer') },
+    { value: 4, text: t('Custom.Recording.ActivitiesApply') },
+    ...(getInfoAgentTransfer.value == 1 ? [{ value: 5, text: t('Home.page_ib.item9') }] : [])
+]));
+const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
+
+// 账户类型映射
+const accountTypeMap = {
+    1: 'AccountType.ClassicAccount',
+    2: 'AccountType.SeniorAccount',
+    5: 'AccountType.SpeedAccount',
+    6: 'AccountType.SpeedAccount',
+    7: 'AccountType.StandardAccount',
+    8: 'AccountType.CentAccount'
+}
+// 拒绝原因映射(示例)
+const reasons = ref({})
+// 根据类型获取列配置
+const getColumnsByType = (type: number) => {
+    switch (type) {
+        case 1: // 开户记录
+            return [
+                {
+                    prop: 'platform',
+                    label: t('Custom.Recording.Platform'),
+                    align: 'left'
+                },
+                {
+                    prop: 'type',
+                    label: t('Custom.PaymentHistory.payType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.currency || '--',
+                },
+                {
+                    prop: 'leverage',
+                    label: t('Custom.Recording.Lever'),
+                    formatter: ({ row }) => `1: ${row.leverage}` || '--',
+                    align: 'left'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.Recording.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 2: // 杠杆修改记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'oldLeverage',
+                    label: t('Custom.Recording.OldLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.oldLeverage ? `1:${row.oldLeverage}` : '--'
+                },
+                {
+                    prop: 'newLeverage',
+                    label: t('Custom.Recording.NewLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.newLeverage ? `1:${row.newLeverage}` : '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 3: // 转账记录
+        case 5: // 内部转账记录
+            return [
+                {
+                    prop: 'withdrawLogin',
+                    label: t('Custom.Recording.TransferAccounts'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawLogin || '--'
+                },
+                {
+                    prop: 'depositLogin',
+                    label: t('Custom.Recording.IntoAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.depositLogin || '--',
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawCurrency || '--',
+                },
+                {
+                    prop: 'withdrawAmount',
+                    label: t('Custom.Recording.Amount'),
+                    align: 'left',
+                    slot: 'amount'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 4: // 活动申请记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'loginType',
+                    label: t('Custom.Recording.AccountType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'title',
+                    label: t('Custom.Recording.ActivityName'),
+                    align: 'left',
+                    formatter: ({ row }) => row.title || '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+    }
+}
+// 动态传入筛选字段配置
+const filterFields = computed(() => [
+    { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: 1 },
+    { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
+])
+const searchParams = ref({})
+const tableRef = ref(null)
+const handleSearch = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+
+const handleReset = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+// 当前列配置
+const currentColumns = computed(() => getColumnsByType(search.value.type))
+// 获取状态文本
+const getStatusText = (row: any) => {
+    const status = row.status
+    // 根据不同记录类型处理状态
+    if (search.value.type === 1) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.accountStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.accountStatus === 1 || !row.accountStatus)) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 2) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.leverageStatus === 2) return t('State.Completed')
+        if (status === 2 && row.leverageStatus === 1) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 3 || search.value.type === 5) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.withdrawStatus === 2 && row.depositStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.withdrawStatus === 1 || row.depositStatus === 1)) return t('State.InTheProcessing')
+        if (status === 3 || row.withdrawStatus === 3 || row.depositStatus === 3) return t('State.Refused')
+    } else {
+        // 活动申请等
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2) return t('State.Completed')
+        if (status === 3) return t('State.Refused')
+    }
+    return ''
+}
+// 获取状态样式类
+const getStatusClass = (status: number) => {
+    const classMap: Record<number, string> = {
+        1: 'status-pending',
+        2: 'status-success',
+        3: 'status-processing',
+        4: 'status-danger'
+    }
+    return classMap[status] || ''
+}
+// 获取账户类型文本
+const getAccountTypeText = (type: number) => {
+    const key = accountTypeMap[type as keyof typeof accountTypeMap]
+    return key ? t(key) : '--'
+}
+// 格式化数字
+const formatNumber = (value: string | number) => {
+    if (!value) return '--'
+    const num = Number(value)
+    return isNaN(num) ? '--' : num.toFixed(2)
+}
+// 格式化备注
+const formatNote = (approveDesc: string) => {
+    if (!approveDesc) return '--'
+    const reason = reasons.value[approveDesc as keyof typeof reasons.value]
+    if (reason) {
+        return isZh.value ? reason.content : reason.enContent
+    }
+    return approveDesc
+}
+const listApi = ref(null)
+listApi.value = customApi.CustomRecordAccount
+</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: #cf1322;
+            ;
+        }
+    }
+}
+
+.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;
+}
+
+.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>

+ 403 - 0
pages/follow/trading-center.vue

@@ -0,0 +1,403 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Documentary.page_doc.item2')" />
+        <view class="info-card">
+            <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
+                @reset="handleReset" />
+            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="search" :api="listApi"
+                :show-operation="false">
+                <!-- 状态列自定义渲染 -->
+                <template #status="{ row }">
+                    <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">
+                        {{ getStatusText(row) }}
+                    </view>
+                    <view v-else></view>
+                </template>
+                <!-- 账户类型列自定义渲染 -->
+                <template #accountType="{ row }">
+                    {{ getAccountTypeText(row.type || row.loginType) }}
+                </template>
+                <!-- 金额列格式化 -->
+                <template #amount="{ row }">
+                    <view>-{{ formatNumber(row.withdrawAmount || row.amount) }}</view>
+                </template>
+                <!-- 备注列格式化 -->
+                <template #note="{ row }">
+                    <view>{{ formatNote(row.approveDesc) }}</view>
+                </template>
+            </cwg-tabel>
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+const { t, locale } = useI18n();
+import { customApi } from '@/service/custom';
+import useUserStore from "@/stores/use-user-store";
+const userStore = useUserStore();
+const userInfo = computed(() => userStore.userInfo);
+const search = ref({
+    type: 1
+})
+const getInfoAgentTransfer = computed(() => userInfo.value.customInfo?.agentTransfer)
+const typeMap = computed(() => ([
+    { value: 1, text: t('Custom.Recording.NewAccount') },
+    { value: 2, text: t('Custom.Recording.LeverageApply') },
+    { value: 3, text: t('Custom.Recording.InternalTransfer') },
+    { value: 4, text: t('Custom.Recording.ActivitiesApply') },
+    ...(getInfoAgentTransfer.value == 1 ? [{ value: 5, text: t('Home.page_ib.item9') }] : [])
+]));
+const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
+
+// 账户类型映射
+const accountTypeMap = {
+    1: 'AccountType.ClassicAccount',
+    2: 'AccountType.SeniorAccount',
+    5: 'AccountType.SpeedAccount',
+    6: 'AccountType.SpeedAccount',
+    7: 'AccountType.StandardAccount',
+    8: 'AccountType.CentAccount'
+}
+// 拒绝原因映射(示例)
+const reasons = ref({})
+// 根据类型获取列配置
+const getColumnsByType = (type: number) => {
+    switch (type) {
+        case 1: // 开户记录
+            return [
+                {
+                    prop: 'platform',
+                    label: t('Custom.Recording.Platform'),
+                    align: 'left'
+                },
+                {
+                    prop: 'type',
+                    label: t('Custom.PaymentHistory.payType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.currency || '--',
+                },
+                {
+                    prop: 'leverage',
+                    label: t('Custom.Recording.Lever'),
+                    formatter: ({ row }) => `1: ${row.leverage}` || '--',
+                    align: 'left'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.Recording.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 2: // 杠杆修改记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'oldLeverage',
+                    label: t('Custom.Recording.OldLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.oldLeverage ? `1:${row.oldLeverage}` : '--'
+                },
+                {
+                    prop: 'newLeverage',
+                    label: t('Custom.Recording.NewLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.newLeverage ? `1:${row.newLeverage}` : '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 3: // 转账记录
+        case 5: // 内部转账记录
+            return [
+                {
+                    prop: 'withdrawLogin',
+                    label: t('Custom.Recording.TransferAccounts'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawLogin || '--'
+                },
+                {
+                    prop: 'depositLogin',
+                    label: t('Custom.Recording.IntoAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.depositLogin || '--',
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawCurrency || '--',
+                },
+                {
+                    prop: 'withdrawAmount',
+                    label: t('Custom.Recording.Amount'),
+                    align: 'left',
+                    slot: 'amount'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 4: // 活动申请记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'loginType',
+                    label: t('Custom.Recording.AccountType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'title',
+                    label: t('Custom.Recording.ActivityName'),
+                    align: 'left',
+                    formatter: ({ row }) => row.title || '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+    }
+}
+// 动态传入筛选字段配置
+const filterFields = computed(() => [
+    { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: 1 },
+    { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
+])
+const searchParams = ref({})
+const tableRef = ref(null)
+const handleSearch = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+
+const handleReset = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+// 当前列配置
+const currentColumns = computed(() => getColumnsByType(search.value.type))
+// 获取状态文本
+const getStatusText = (row: any) => {
+    const status = row.status
+    // 根据不同记录类型处理状态
+    if (search.value.type === 1) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.accountStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.accountStatus === 1 || !row.accountStatus)) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 2) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.leverageStatus === 2) return t('State.Completed')
+        if (status === 2 && row.leverageStatus === 1) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 3 || search.value.type === 5) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.withdrawStatus === 2 && row.depositStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.withdrawStatus === 1 || row.depositStatus === 1)) return t('State.InTheProcessing')
+        if (status === 3 || row.withdrawStatus === 3 || row.depositStatus === 3) return t('State.Refused')
+    } else {
+        // 活动申请等
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2) return t('State.Completed')
+        if (status === 3) return t('State.Refused')
+    }
+    return ''
+}
+// 获取状态样式类
+const getStatusClass = (status: number) => {
+    const classMap: Record<number, string> = {
+        1: 'status-pending',
+        2: 'status-success',
+        3: 'status-processing',
+        4: 'status-danger'
+    }
+    return classMap[status] || ''
+}
+// 获取账户类型文本
+const getAccountTypeText = (type: number) => {
+    const key = accountTypeMap[type as keyof typeof accountTypeMap]
+    return key ? t(key) : '--'
+}
+// 格式化数字
+const formatNumber = (value: string | number) => {
+    if (!value) return '--'
+    const num = Number(value)
+    return isNaN(num) ? '--' : num.toFixed(2)
+}
+// 格式化备注
+const formatNote = (approveDesc: string) => {
+    if (!approveDesc) return '--'
+    const reason = reasons.value[approveDesc as keyof typeof reasons.value]
+    if (reason) {
+        return isZh.value ? reason.content : reason.enContent
+    }
+    return approveDesc
+}
+const listApi = ref(null)
+listApi.value = customApi.CustomRecordAccount
+</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: #cf1322;
+            ;
+        }
+    }
+}
+
+.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;
+}
+
+.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>

+ 403 - 0
pages/follow/trading-management.vue

@@ -0,0 +1,403 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Documentary.TundManagement.item8')" />
+        <view class="info-card">
+            <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
+                @reset="handleReset" />
+            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="search" :api="listApi"
+                :show-operation="false">
+                <!-- 状态列自定义渲染 -->
+                <template #status="{ row }">
+                    <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">
+                        {{ getStatusText(row) }}
+                    </view>
+                    <view v-else></view>
+                </template>
+                <!-- 账户类型列自定义渲染 -->
+                <template #accountType="{ row }">
+                    {{ getAccountTypeText(row.type || row.loginType) }}
+                </template>
+                <!-- 金额列格式化 -->
+                <template #amount="{ row }">
+                    <view>-{{ formatNumber(row.withdrawAmount || row.amount) }}</view>
+                </template>
+                <!-- 备注列格式化 -->
+                <template #note="{ row }">
+                    <view>{{ formatNote(row.approveDesc) }}</view>
+                </template>
+            </cwg-tabel>
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+const { t, locale } = useI18n();
+import { customApi } from '@/service/custom';
+import useUserStore from "@/stores/use-user-store";
+const userStore = useUserStore();
+const userInfo = computed(() => userStore.userInfo);
+const search = ref({
+    type: 1
+})
+const getInfoAgentTransfer = computed(() => userInfo.value.customInfo?.agentTransfer)
+const typeMap = computed(() => ([
+    { value: 1, text: t('Custom.Recording.NewAccount') },
+    { value: 2, text: t('Custom.Recording.LeverageApply') },
+    { value: 3, text: t('Custom.Recording.InternalTransfer') },
+    { value: 4, text: t('Custom.Recording.ActivitiesApply') },
+    ...(getInfoAgentTransfer.value == 1 ? [{ value: 5, text: t('Home.page_ib.item9') }] : [])
+]));
+const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
+
+// 账户类型映射
+const accountTypeMap = {
+    1: 'AccountType.ClassicAccount',
+    2: 'AccountType.SeniorAccount',
+    5: 'AccountType.SpeedAccount',
+    6: 'AccountType.SpeedAccount',
+    7: 'AccountType.StandardAccount',
+    8: 'AccountType.CentAccount'
+}
+// 拒绝原因映射(示例)
+const reasons = ref({})
+// 根据类型获取列配置
+const getColumnsByType = (type: number) => {
+    switch (type) {
+        case 1: // 开户记录
+            return [
+                {
+                    prop: 'platform',
+                    label: t('Custom.Recording.Platform'),
+                    align: 'left'
+                },
+                {
+                    prop: 'type',
+                    label: t('Custom.PaymentHistory.payType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.currency || '--',
+                },
+                {
+                    prop: 'leverage',
+                    label: t('Custom.Recording.Lever'),
+                    formatter: ({ row }) => `1: ${row.leverage}` || '--',
+                    align: 'left'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.Recording.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 2: // 杠杆修改记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'oldLeverage',
+                    label: t('Custom.Recording.OldLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.oldLeverage ? `1:${row.oldLeverage}` : '--'
+                },
+                {
+                    prop: 'newLeverage',
+                    label: t('Custom.Recording.NewLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.newLeverage ? `1:${row.newLeverage}` : '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 3: // 转账记录
+        case 5: // 内部转账记录
+            return [
+                {
+                    prop: 'withdrawLogin',
+                    label: t('Custom.Recording.TransferAccounts'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawLogin || '--'
+                },
+                {
+                    prop: 'depositLogin',
+                    label: t('Custom.Recording.IntoAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.depositLogin || '--',
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawCurrency || '--',
+                },
+                {
+                    prop: 'withdrawAmount',
+                    label: t('Custom.Recording.Amount'),
+                    align: 'left',
+                    slot: 'amount'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 4: // 活动申请记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'loginType',
+                    label: t('Custom.Recording.AccountType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'title',
+                    label: t('Custom.Recording.ActivityName'),
+                    align: 'left',
+                    formatter: ({ row }) => row.title || '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+    }
+}
+// 动态传入筛选字段配置
+const filterFields = computed(() => [
+    { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: 1 },
+    { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
+])
+const searchParams = ref({})
+const tableRef = ref(null)
+const handleSearch = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+
+const handleReset = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+// 当前列配置
+const currentColumns = computed(() => getColumnsByType(search.value.type))
+// 获取状态文本
+const getStatusText = (row: any) => {
+    const status = row.status
+    // 根据不同记录类型处理状态
+    if (search.value.type === 1) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.accountStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.accountStatus === 1 || !row.accountStatus)) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 2) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.leverageStatus === 2) return t('State.Completed')
+        if (status === 2 && row.leverageStatus === 1) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 3 || search.value.type === 5) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.withdrawStatus === 2 && row.depositStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.withdrawStatus === 1 || row.depositStatus === 1)) return t('State.InTheProcessing')
+        if (status === 3 || row.withdrawStatus === 3 || row.depositStatus === 3) return t('State.Refused')
+    } else {
+        // 活动申请等
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2) return t('State.Completed')
+        if (status === 3) return t('State.Refused')
+    }
+    return ''
+}
+// 获取状态样式类
+const getStatusClass = (status: number) => {
+    const classMap: Record<number, string> = {
+        1: 'status-pending',
+        2: 'status-success',
+        3: 'status-processing',
+        4: 'status-danger'
+    }
+    return classMap[status] || ''
+}
+// 获取账户类型文本
+const getAccountTypeText = (type: number) => {
+    const key = accountTypeMap[type as keyof typeof accountTypeMap]
+    return key ? t(key) : '--'
+}
+// 格式化数字
+const formatNumber = (value: string | number) => {
+    if (!value) return '--'
+    const num = Number(value)
+    return isNaN(num) ? '--' : num.toFixed(2)
+}
+// 格式化备注
+const formatNote = (approveDesc: string) => {
+    if (!approveDesc) return '--'
+    const reason = reasons.value[approveDesc as keyof typeof reasons.value]
+    if (reason) {
+        return isZh.value ? reason.content : reason.enContent
+    }
+    return approveDesc
+}
+const listApi = ref(null)
+listApi.value = customApi.CustomRecordAccount
+</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: #cf1322;
+            ;
+        }
+    }
+}
+
+.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;
+}
+
+.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>

+ 403 - 0
pages/follow/transfer-history.vue

@@ -0,0 +1,403 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Documentary.TundManagement.item10')" />
+        <view class="info-card">
+            <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
+                @reset="handleReset" />
+            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="search" :api="listApi"
+                :show-operation="false">
+                <!-- 状态列自定义渲染 -->
+                <template #status="{ row }">
+                    <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">
+                        {{ getStatusText(row) }}
+                    </view>
+                    <view v-else></view>
+                </template>
+                <!-- 账户类型列自定义渲染 -->
+                <template #accountType="{ row }">
+                    {{ getAccountTypeText(row.type || row.loginType) }}
+                </template>
+                <!-- 金额列格式化 -->
+                <template #amount="{ row }">
+                    <view>-{{ formatNumber(row.withdrawAmount || row.amount) }}</view>
+                </template>
+                <!-- 备注列格式化 -->
+                <template #note="{ row }">
+                    <view>{{ formatNote(row.approveDesc) }}</view>
+                </template>
+            </cwg-tabel>
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+const { t, locale } = useI18n();
+import { customApi } from '@/service/custom';
+import useUserStore from "@/stores/use-user-store";
+const userStore = useUserStore();
+const userInfo = computed(() => userStore.userInfo);
+const search = ref({
+    type: 1
+})
+const getInfoAgentTransfer = computed(() => userInfo.value.customInfo?.agentTransfer)
+const typeMap = computed(() => ([
+    { value: 1, text: t('Custom.Recording.NewAccount') },
+    { value: 2, text: t('Custom.Recording.LeverageApply') },
+    { value: 3, text: t('Custom.Recording.InternalTransfer') },
+    { value: 4, text: t('Custom.Recording.ActivitiesApply') },
+    ...(getInfoAgentTransfer.value == 1 ? [{ value: 5, text: t('Home.page_ib.item9') }] : [])
+]));
+const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
+
+// 账户类型映射
+const accountTypeMap = {
+    1: 'AccountType.ClassicAccount',
+    2: 'AccountType.SeniorAccount',
+    5: 'AccountType.SpeedAccount',
+    6: 'AccountType.SpeedAccount',
+    7: 'AccountType.StandardAccount',
+    8: 'AccountType.CentAccount'
+}
+// 拒绝原因映射(示例)
+const reasons = ref({})
+// 根据类型获取列配置
+const getColumnsByType = (type: number) => {
+    switch (type) {
+        case 1: // 开户记录
+            return [
+                {
+                    prop: 'platform',
+                    label: t('Custom.Recording.Platform'),
+                    align: 'left'
+                },
+                {
+                    prop: 'type',
+                    label: t('Custom.PaymentHistory.payType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.currency || '--',
+                },
+                {
+                    prop: 'leverage',
+                    label: t('Custom.Recording.Lever'),
+                    formatter: ({ row }) => `1: ${row.leverage}` || '--',
+                    align: 'left'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.Recording.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 2: // 杠杆修改记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'oldLeverage',
+                    label: t('Custom.Recording.OldLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.oldLeverage ? `1:${row.oldLeverage}` : '--'
+                },
+                {
+                    prop: 'newLeverage',
+                    label: t('Custom.Recording.NewLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.newLeverage ? `1:${row.newLeverage}` : '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 3: // 转账记录
+        case 5: // 内部转账记录
+            return [
+                {
+                    prop: 'withdrawLogin',
+                    label: t('Custom.Recording.TransferAccounts'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawLogin || '--'
+                },
+                {
+                    prop: 'depositLogin',
+                    label: t('Custom.Recording.IntoAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.depositLogin || '--',
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawCurrency || '--',
+                },
+                {
+                    prop: 'withdrawAmount',
+                    label: t('Custom.Recording.Amount'),
+                    align: 'left',
+                    slot: 'amount'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 4: // 活动申请记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'loginType',
+                    label: t('Custom.Recording.AccountType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'title',
+                    label: t('Custom.Recording.ActivityName'),
+                    align: 'left',
+                    formatter: ({ row }) => row.title || '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+    }
+}
+// 动态传入筛选字段配置
+const filterFields = computed(() => [
+    { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: 1 },
+    { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
+])
+const searchParams = ref({})
+const tableRef = ref(null)
+const handleSearch = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+
+const handleReset = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+// 当前列配置
+const currentColumns = computed(() => getColumnsByType(search.value.type))
+// 获取状态文本
+const getStatusText = (row: any) => {
+    const status = row.status
+    // 根据不同记录类型处理状态
+    if (search.value.type === 1) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.accountStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.accountStatus === 1 || !row.accountStatus)) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 2) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.leverageStatus === 2) return t('State.Completed')
+        if (status === 2 && row.leverageStatus === 1) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 3 || search.value.type === 5) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.withdrawStatus === 2 && row.depositStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.withdrawStatus === 1 || row.depositStatus === 1)) return t('State.InTheProcessing')
+        if (status === 3 || row.withdrawStatus === 3 || row.depositStatus === 3) return t('State.Refused')
+    } else {
+        // 活动申请等
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2) return t('State.Completed')
+        if (status === 3) return t('State.Refused')
+    }
+    return ''
+}
+// 获取状态样式类
+const getStatusClass = (status: number) => {
+    const classMap: Record<number, string> = {
+        1: 'status-pending',
+        2: 'status-success',
+        3: 'status-processing',
+        4: 'status-danger'
+    }
+    return classMap[status] || ''
+}
+// 获取账户类型文本
+const getAccountTypeText = (type: number) => {
+    const key = accountTypeMap[type as keyof typeof accountTypeMap]
+    return key ? t(key) : '--'
+}
+// 格式化数字
+const formatNumber = (value: string | number) => {
+    if (!value) return '--'
+    const num = Number(value)
+    return isNaN(num) ? '--' : num.toFixed(2)
+}
+// 格式化备注
+const formatNote = (approveDesc: string) => {
+    if (!approveDesc) return '--'
+    const reason = reasons.value[approveDesc as keyof typeof reasons.value]
+    if (reason) {
+        return isZh.value ? reason.content : reason.enContent
+    }
+    return approveDesc
+}
+const listApi = ref(null)
+listApi.value = customApi.CustomRecordAccount
+</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: #cf1322;
+            ;
+        }
+    }
+}
+
+.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;
+}
+
+.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>

+ 403 - 0
pages/follow/transfer.vue

@@ -0,0 +1,403 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Documentary.TundManagement.item2')" />
+        <view class="info-card">
+            <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
+                @reset="handleReset" />
+            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="search" :api="listApi"
+                :show-operation="false">
+                <!-- 状态列自定义渲染 -->
+                <template #status="{ row }">
+                    <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">
+                        {{ getStatusText(row) }}
+                    </view>
+                    <view v-else></view>
+                </template>
+                <!-- 账户类型列自定义渲染 -->
+                <template #accountType="{ row }">
+                    {{ getAccountTypeText(row.type || row.loginType) }}
+                </template>
+                <!-- 金额列格式化 -->
+                <template #amount="{ row }">
+                    <view>-{{ formatNumber(row.withdrawAmount || row.amount) }}</view>
+                </template>
+                <!-- 备注列格式化 -->
+                <template #note="{ row }">
+                    <view>{{ formatNote(row.approveDesc) }}</view>
+                </template>
+            </cwg-tabel>
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, nextTick } from 'vue';
+import { useI18n } from 'vue-i18n';
+const { t, locale } = useI18n();
+import { customApi } from '@/service/custom';
+import useUserStore from "@/stores/use-user-store";
+const userStore = useUserStore();
+const userInfo = computed(() => userStore.userInfo);
+const search = ref({
+    type: 1
+})
+const getInfoAgentTransfer = computed(() => userInfo.value.customInfo?.agentTransfer)
+const typeMap = computed(() => ([
+    { value: 1, text: t('Custom.Recording.NewAccount') },
+    { value: 2, text: t('Custom.Recording.LeverageApply') },
+    { value: 3, text: t('Custom.Recording.InternalTransfer') },
+    { value: 4, text: t('Custom.Recording.ActivitiesApply') },
+    ...(getInfoAgentTransfer.value == 1 ? [{ value: 5, text: t('Home.page_ib.item9') }] : [])
+]));
+const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
+
+// 账户类型映射
+const accountTypeMap = {
+    1: 'AccountType.ClassicAccount',
+    2: 'AccountType.SeniorAccount',
+    5: 'AccountType.SpeedAccount',
+    6: 'AccountType.SpeedAccount',
+    7: 'AccountType.StandardAccount',
+    8: 'AccountType.CentAccount'
+}
+// 拒绝原因映射(示例)
+const reasons = ref({})
+// 根据类型获取列配置
+const getColumnsByType = (type: number) => {
+    switch (type) {
+        case 1: // 开户记录
+            return [
+                {
+                    prop: 'platform',
+                    label: t('Custom.Recording.Platform'),
+                    align: 'left'
+                },
+                {
+                    prop: 'type',
+                    label: t('Custom.PaymentHistory.payType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.currency || '--',
+                },
+                {
+                    prop: 'leverage',
+                    label: t('Custom.Recording.Lever'),
+                    formatter: ({ row }) => `1: ${row.leverage}` || '--',
+                    align: 'left'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.Recording.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 2: // 杠杆修改记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'oldLeverage',
+                    label: t('Custom.Recording.OldLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.oldLeverage ? `1:${row.oldLeverage}` : '--'
+                },
+                {
+                    prop: 'newLeverage',
+                    label: t('Custom.Recording.NewLever'),
+                    align: 'left',
+                    formatter: ({ row }) => row.newLeverage ? `1:${row.newLeverage}` : '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 3: // 转账记录
+        case 5: // 内部转账记录
+            return [
+                {
+                    prop: 'withdrawLogin',
+                    label: t('Custom.Recording.TransferAccounts'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawLogin || '--'
+                },
+                {
+                    prop: 'depositLogin',
+                    label: t('Custom.Recording.IntoAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.depositLogin || '--',
+                },
+                {
+                    prop: 'withdrawCurrency',
+                    label: t('Custom.Recording.CurrencyType'),
+                    align: 'left',
+                    formatter: ({ row }) => row.withdrawCurrency || '--',
+                },
+                {
+                    prop: 'withdrawAmount',
+                    label: t('Custom.Recording.Amount'),
+                    align: 'left',
+                    slot: 'amount'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+        case 4: // 活动申请记录
+            return [
+                {
+                    prop: 'login',
+                    label: t('Custom.Recording.TradingAccount'),
+                    align: 'left',
+                    formatter: ({ row }) => row.login || '--'
+                },
+                {
+                    prop: 'loginType',
+                    label: t('Custom.Recording.AccountType'),
+                    align: 'left',
+                    slot: 'accountType'
+                },
+                {
+                    prop: 'title',
+                    label: t('Custom.Recording.ActivityName'),
+                    align: 'left',
+                    formatter: ({ row }) => row.title || '--'
+                },
+                {
+                    prop: 'addTime',
+                    label: t('Custom.PaymentHistory.ApplicationDate'),
+                    type: 'date',
+                    dateFormat: 'YYYY-MM-DD HH:mm',
+                    align: 'left'
+                },
+                {
+                    prop: 'status',
+                    label: t('Custom.PaymentHistory.Status'),
+                    slot: 'status',
+                    align: 'left'
+                },
+                {
+                    prop: 'note',
+                    label: t('Custom.Recording.Note'),
+                    type: 'note',
+                    align: 'left'
+                }
+            ]
+    }
+}
+// 动态传入筛选字段配置
+const filterFields = computed(() => [
+    { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: 1 },
+    { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
+])
+const searchParams = ref({})
+const tableRef = ref(null)
+const handleSearch = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+
+const handleReset = (params) => {
+    search.value = params
+    nextTick(() => {
+        tableRef.value.refreshTable()
+    })
+}
+// 当前列配置
+const currentColumns = computed(() => getColumnsByType(search.value.type))
+// 获取状态文本
+const getStatusText = (row: any) => {
+    const status = row.status
+    // 根据不同记录类型处理状态
+    if (search.value.type === 1) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.accountStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.accountStatus === 1 || !row.accountStatus)) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 2) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.leverageStatus === 2) return t('State.Completed')
+        if (status === 2 && row.leverageStatus === 1) return t('State.InTheProcessing')
+        if (status === 3) return t('State.Refused')
+    } else if (search.value.type === 3 || search.value.type === 5) {
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2 && row.withdrawStatus === 2 && row.depositStatus === 2) return t('State.Completed')
+        if (status === 2 && (row.withdrawStatus === 1 || row.depositStatus === 1)) return t('State.InTheProcessing')
+        if (status === 3 || row.withdrawStatus === 3 || row.depositStatus === 3) return t('State.Refused')
+    } else {
+        // 活动申请等
+        if (status === 1) return t('State.ToBeProcessed')
+        if (status === 2) return t('State.Completed')
+        if (status === 3) return t('State.Refused')
+    }
+    return ''
+}
+// 获取状态样式类
+const getStatusClass = (status: number) => {
+    const classMap: Record<number, string> = {
+        1: 'status-pending',
+        2: 'status-success',
+        3: 'status-processing',
+        4: 'status-danger'
+    }
+    return classMap[status] || ''
+}
+// 获取账户类型文本
+const getAccountTypeText = (type: number) => {
+    const key = accountTypeMap[type as keyof typeof accountTypeMap]
+    return key ? t(key) : '--'
+}
+// 格式化数字
+const formatNumber = (value: string | number) => {
+    if (!value) return '--'
+    const num = Number(value)
+    return isNaN(num) ? '--' : num.toFixed(2)
+}
+// 格式化备注
+const formatNote = (approveDesc: string) => {
+    if (!approveDesc) return '--'
+    const reason = reasons.value[approveDesc as keyof typeof reasons.value]
+    if (reason) {
+        return isZh.value ? reason.content : reason.enContent
+    }
+    return approveDesc
+}
+const listApi = ref(null)
+listApi.value = customApi.CustomRecordAccount
+</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: #cf1322;
+            ;
+        }
+    }
+}
+
+.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;
+}
+
+.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>

+ 1 - 0
static/icons/crm-gd.svg

@@ -0,0 +1 @@
+<svg class="IconContainer_icon__XfQWv" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 24 24"><path fill="none" d="M5 21C5.65293 18.6915 7.98204 17 10.4996 17C13.0171 17 15.3471 18.6915 16 21M20.9996 15L20.9999 6.001C20.9999 3.79199 19.2093 2.00112 17.0003 2.00088L8.9996 2M12.9996 11.5C12.9996 12.8807 11.8803 14 10.4996 14C9.11888 14 7.9996 12.8807 7.9996 11.5C7.9996 10.1193 9.11888 9 10.4996 9C11.8803 9 12.9996 10.1193 12.9996 11.5ZM16.9996 7C16.9996 11.3333 16.9996 15.6667 16.9996 20C16.9996 20.5523 16.5511 21 15.9988 21C11.9043 21 9.09511 21 5.00056 21C4.44828 21 3.99979 20.5523 3.99976 20C3.99953 15.6665 3.99832 11.3331 3.9996 6.99961C3.99977 6.44748 4.44819 6 5.00033 6C9.09503 6 11.9042 6 15.9988 6C16.5511 6 16.9996 6.44772 16.9996 7Z" stroke="black" data-stroke="" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>

+ 4 - 0
static/svg-icons-lib.js

@@ -147,6 +147,10 @@ const collections = {
         "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"#22ac38\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" class=\"tabler-icon-external-link\" viewBox=\"0 0 24 24\"><path d=\"M12 6H6a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-6m-7 1 9-9m-5 0h5v5\"/></svg>",
         2
       ],
+      "crm-gd": [
+        "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" class=\"IconContainer_icon__XfQWv\" viewBox=\"0 0 24 24\"><path fill=\"none\" stroke=\"#000\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 21c.653-2.308 2.982-4 5.5-4s4.847 1.692 5.5 4m5-6V6.001a4 4 0 0 0-4-4L9 2m4 9.5a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0M17 7v13a1 1 0 0 1-1.001 1H5a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h11a1 1 0 0 1 1 1\"/></svg>",
+        6
+      ],
       "crm-gear": [
         "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 640 640\"><!--!Font Awesome Free 7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.--><path fill=\"#22ac38\" d=\"M259.1 73.5c3-14.8 16.1-25.5 31.3-25.5h59.8c15.2 0 28.3 10.7 31.3 25.5l14.5 70c14.1 6 27.3 13.7 39.3 22.8l67.8-22.5c14.4-4.8 30.2 1.2 37.8 14.4l29.9 51.8c7.6 13.2 4.9 29.8-6.5 39.9L511 297.3c.9 7.4 1.3 15 1.3 22.7s-.5 15.3-1.3 22.7l53.4 47.5c11.4 10.1 14 26.8 6.5 39.9L541 481.9c-7.6 13.1-23.4 19.2-37.8 14.4l-67.8-22.5c-12.1 9.1-25.3 16.7-39.3 22.8l-14.4 69.9c-3.1 14.9-16.2 25.5-31.3 25.5h-59.8c-15.2 0-28.3-10.7-31.3-25.5l-14.4-69.9c-14.1-6-27.2-13.7-39.3-22.8l-68.1 22.5c-14.4 4.8-30.2-1.2-37.8-14.4l-29.9-51.8c-7.6-13.2-4.9-29.8 6.5-39.9l53.4-47.5c-.9-7.4-1.3-15-1.3-22.7s.5-15.3 1.3-22.7l-53.4-47.5c-11.4-10.1-14-26.8-6.5-39.9l29.9-51.8c7.6-13.2 23.4-19.2 37.8-14.4l67.8 22.5c12.1-9.1 25.3-16.7 39.3-22.8zM320.3 400c44.2-.2 79.9-36.1 79.7-80.3s-36.1-79.9-80.3-79.7-79.9 36.1-79.7 80.3 36.1 79.9 80.3 79.7\"/></svg>",
         2

+ 7 - 3
windows/left-window.vue

@@ -20,13 +20,17 @@
       </view>
     </view>
     <view class="menu fixed">
+      <view class="menu-item ib-box" @click="setMode('customer')" v-if="mode !== 'customer'">
+        <cwg-icon name="crm-trade" :size="20" color="#6c8595" />
+        <view class="menu-label" v-t="'Home.msg.Custom'" />
+      </view>
       <view class="menu-item ib-box" @click="setMode('ib')" v-if="mode !== 'ib' && ibStatus">
         <cwg-icon name="crm-ib" :size="20" color="#6c8595" />
         <view class="menu-label" v-t="'Home.msg.Ib'" />
       </view>
-      <view class="menu-item ib-box" @click="setMode('customer')" v-if="mode !== 'customer'">
-        <cwg-icon name="crm-trade" :size="20" color="#6c8595" />
-        <view class="menu-label" v-t="'Home.msg.Custom'" />
+      <view class="menu-item ib-box" @click="setMode('follow')" v-if="mode !== 'follow'">
+        <cwg-icon name="crm-gd" :size="20" color="#6c8595" />
+        <view class="menu-label" v-t="'Documentary.title'" />
       </view>
       <view class="menu-item zy-box" @click="handleFixedMenuClick" :class="{ 'active': isCollapsed }">
         <cwg-icon name="crm-zy" :size="20" color="#6c8595" />