zhb 2 meses atrás
pai
commit
131e3d9f1e

+ 12 - 5
components/cwg-complex-search.vue

@@ -13,7 +13,8 @@
                             </template>
                             <template v-else-if="field.type === 'select'">
                                 <cwg-combox v-model:value="formData[field.key]" :options="field.options"
-                                    :placeholder="field.placeholder || '请选择'" :clearable="false" @change="handleSearch" />
+                                    :placeholder="field.placeholder || '请选择'" :clearable="false"
+                                    @change="handleSearch" />
                             </template>
                             <template v-else-if="field.type === 'date'">
                                 <uni-datetime-picker v-model="formData[field.key]" type="date"
@@ -77,7 +78,8 @@
                         </template>
                         <template v-else-if="field.type === 'select'">
                             <uni-data-select v-model:value="tempFormData[field.key]" :localdata="field.options"
-                                :placeholder="field.placeholder || '请选择'" :clearable="false" v-if="shouldUseSelect(field)" />
+                                :placeholder="field.placeholder || '请选择'" :clearable="false"
+                                v-if="shouldUseSelect(field)" />
                             <view class="chip-group" v-else>
                                 <view class="chip-list">
                                     <view v-for="opt in field.options" :key="opt.value" class="chip" :class="{
@@ -136,7 +138,7 @@ const props = defineProps({
 })
 
 const emit = defineEmits(['update:modelValue', 'search', 'reset'])
-console.log(props.fields,12);
+console.log(props.fields, 12);
 
 // 获取日期字段的默认值(当前月范围或当天)
 const getDefaultDateValue = (field) => {
@@ -200,8 +202,8 @@ const initFormData = () => {
         }
         // 4. select 类型字段特殊处理:如果没有默认值,默认选择第一个选项
         else if (field.type === 'select' && field.options && field.options.length > 0) {
-            console.log(23,field.options,field,111);
-            
+            console.log(23, field.options, field, 111);
+
             initial[field.key] = field.options[0].value
         }
         // 5. 其他字段默认为空字符串
@@ -308,6 +310,11 @@ const resetForm = () => {
 }
 // 触发查询
 const handleSearch = () => {
+    console.log(formData.value, 1212);
+
+    // if (formData.value.login) {
+    //     formData.value.login = Number(formData.value.login)
+    // }
     emit('search', { ...formData.value })
 }
 

+ 3 - 2
components/cwg-dropdown.vue

@@ -5,8 +5,10 @@
         </view>
         <view class="cwg-dropdown-menu">
             <view class="cwg-dropdown-menu-container" :style="[layout]" @click.stop>
+
                 <slot name="menu">
                     <view class="menu">
+                        <slot name="btn"></slot>
                         <view class="menu-item" v-for="(item, idx) in menuList" :key="idx"
                             @click="menuClick(item, idx)">
                             <view>{{ item.label || item }}</view>
@@ -176,7 +178,7 @@ onMounted(() => {
         overflow: hidden;
 
         .menu-item {
-            width: px2rpx(150);
+            min-width: px2rpx(150);
             display: flex;
             align-items: center;
             padding: px2rpx(10) px2rpx(16);
@@ -184,7 +186,6 @@ onMounted(() => {
             line-height: px2rpx(30);
             color: #333;
             transition: background 0.2s;
-            border-bottom: 1px solid #f0f0f0;
             box-sizing: border-box;
 
             &:last-child {

+ 14 - 6
components/cwg-tabel.vue

@@ -98,7 +98,7 @@
         </view>
         <!-- 分页 -->
         <view class="pagination-container" v-if="showPagination && tableData.length > 0">
-            <view class="pagination-info">
+            <!-- <view class="pagination-info">
                 <text>共 {{ pagination.total }} 条记录</text>
                 <view v-if="showPageSize" class="page-size-select">
                     <text>每页显示</text>
@@ -106,12 +106,12 @@
                         <view class="page-size-value">{{ pagination.pageSize }}条/页</view>
                     </picker>
                 </view>
-            </view>
+            </view> -->
             <view class="pagination">
                 <view class="page-item prev" :class="{ disabled: pagination.current === 1 }"
                     @click="handlePageChange('prev')">
-                    <uni-icons type="arrowleft" size="16" color="#666" />
-                    <text>上一页</text>
+                    <uni-icons type="arrowright" size="16" color="#666" class="arrow-left" />
+                    <!-- <text>上一页</text> -->
                 </view>
 
                 <view class="page-numbers">
@@ -123,7 +123,7 @@
 
                 <view class="page-item next" :class="{ disabled: pagination.current === pagination.pages }"
                     @click="handlePageChange('next')">
-                    <text>下一页</text>
+                    <!-- <text>下一页</text> -->
                     <uni-icons type="arrowright" size="16" color="#666" />
                 </view>
             </view>
@@ -187,7 +187,7 @@ const props = defineProps({
     showPagination: { type: Boolean, default: true },
     isViewDetail: { type: Boolean, default: true },
     // 每页条数选项
-    pageSizes: { type: Array, default: () => [10, 20, 30, 50, 100] },
+    pageSizes: { type: Array, default: () => [2, 4, 6, 8, 10, 20, 30, 50, 100] },
     // 默认每页条数
     defaultPageSize: { type: Number, default: 10 },
     // 表头样式自定义
@@ -562,6 +562,7 @@ const loadData = async () => {
         emit('load-error', error)
     } finally {
         loading.value = false
+        pageLoading.value = false
     }
 }
 // 刷新表格
@@ -572,8 +573,11 @@ const refreshTable = () => {
 const reload = () => {
     loadData()
 }
+const pageLoading = ref(false)
 // 分页变化
 const handlePageChange = (page) => {
+    if (pageLoading.value) return
+    pageLoading.value = true
     if (page === 'prev') {
         if (pagination.value.current > 1) pagination.value.current--
         else return
@@ -809,6 +813,10 @@ defineExpose({
         gap: px2rpx(8);
     }
 
+    .arrow-left {
+        transform: rotate(180deg);
+    }
+
     .page-number {
         display: flex;
         align-items: center;

+ 1 - 1
manifest.json

@@ -1,5 +1,5 @@
 {
-    "name" : "C Pays",
+    "name" : "CWGMarkets",
     "appid" : "__UNI__60A9455",
     "description" : "应用描述",
     "versionName" : "1.0.5",

+ 681 - 0
pages/customer/components/AccountCard copy.vue

@@ -0,0 +1,681 @@
+<template>
+    <view class="account-card" :class="{ 'is-grid-layout': isGridLayout }">
+        <!-- 折叠/展开按钮 -->
+        <button class="collapse-btn" @click="toggleExpand">
+            <cwg-icon name="chevron-right" :size="20" color="#6c8595"
+                :class="['chevron-icon', { expanded: isExpanded }]" />
+        </button>
+
+        <!-- 标签区域 -->
+        <view class="labels-container">
+            <view class="account-number"># {{ account.accountNumber }}</view>
+            <view class="account-number" @click="copy(account.fwq)">{{ account.fwq }}</view>
+            <view class="labels">
+                <template v-for="(label, index) in account.labels" :key="index">
+                    <view v-if="label" class="label-badge">
+                        {{ label }}
+                    </view>
+                </template>
+            </view>
+        </view>
+
+        <!-- 主要内容区域(余额和操作按钮) -->
+        <view class="main-content">
+            <!-- 余额 -->
+            <view class="balance">
+                <text class="balance-number">{{ balanceInteger }}</text>
+                <text class="balance-decimal">{{ balanceDecimal }} {{ account.currency }}</text>
+            </view>
+
+            <!-- 移动端圆形按钮组(≤1100px显示) -->
+            <view class="mobile-buttons">
+                <!-- 交易 -->
+                <template v-for="(item, index) in circleButtons" :key="index">
+                    <view class="circle-btn" :class="{ 'is-disabled': item.disabled }" @click="handleAction1(item)"
+                        v-if="!item.needDemo">
+                        <view class="circle-icon" :class="{ 'primary': item.primary }">
+                            <cwg-icon :name="item.icon" :size="16" :color="item.color" />
+                        </view>
+                        <text class="circle-label" v-t="item.label" />
+                    </view>
+                </template>
+                <!-- 更多(三点) -->
+                <cwg-dropdown @open="onOpen" @close="onClose" :menu-list="customMenuList"
+                    @menuClick="handleCustomClick">
+                    <view class="circle-btn">
+                        <view class="circle-icon">
+                            <cwg-icon name="crm-ellipsis-vertical" :size="16" color="#2e3a47" />
+                        </view>
+                        <text class="circle-label" v-t="'vu.item7'" />
+                    </view>
+                </cwg-dropdown>
+            </view>
+
+            <!-- 桌面端按钮组(>1100px显示且非网格布局) -->
+            <view class="desktop-buttons" v-if="!props.isGridLayout">
+                <template v-for="(item, index) in actionButtons" :key="index">
+                    <view class="action-btn" :class="{ 'primary': item.primary, 'is-disabled': item.disabled }"
+                        @click="handleAction1(item)" v-if="!item.needDemo">
+                        <text class="btn-icon">
+                            <cwg-icon :name="item.icon" :size="16" :color="item.color" />
+                        </text>
+                        <text v-t="item.label" />
+                    </view>
+                </template>
+                <cwg-dropdown @open="onOpen" @close="onClose" :menu-list="customMenuList"
+                    @menuClick="handleCustomClick">
+                    <view class="action-btn icon-only">
+                        <cwg-icon name="crm-ellipsis-vertical" :size="16" color="#2e3a47" />
+                    </view>
+                </cwg-dropdown>
+            </view>
+        </view>
+        <view ref="infoBottomRef" class="info-bottom" :style="{
+            height: isExpanded ? infoBottomHeight + 'px' : '0px',
+            transition: 'height 281ms cubic-bezier(0.4, 0, 0.2, 1)'
+        }">
+            <!-- 底部信息区域(折叠时隐藏) -->
+            <view class="info-section">
+                <view class="info-column">
+                    <cwg-label-line-value :label="t('Label.Leverage')" :value="account.actualLeverage" />
+                    <cwg-label-line-value :label="t('Label.FloatingPL')" :value="account.floatingPL" />
+                    <cwg-label-line-value :label="t('Label.Balance')" :value="account.balanceWithSymbol" />
+                </view>
+                <view class="info-column">
+                    <cwg-label-line-value :label="t('Label.Equity')" :value="account.equityWithSymbol" />
+                    <cwg-label-line-value :label="t('Label.Credit')" :value="account.creditWithSymbol" />
+                    <cwg-label-line-value :label="t('Documentary.console.item3')" :value="account.platform" />
+                </view>
+            </view>
+            <!-- 额外操作行(服务器、登录、更改密码,折叠时隐藏) -->
+            <view class="extra-actions">
+                <!-- 登录复制行 -->
+                <view class="copy-row">
+                    <text class="label">服务器</text>
+                    <view class="value">{{ account.fwq }}</view>
+                    <view class="copy-btn" @click="copy(account.fwq)">
+                        <cwg-icon name="copy" :size="16" color="#2e3a47" />
+                    </view>
+                </view>
+                <!-- <view class="divider"></view> -->
+                <view class="copy-row">
+                    <text class="label" v-t="'signin.title'" />
+                    <text class="value">{{ account.login }}</text>
+                    <view class="copy-btn" @click="copy(account.login)">
+                        <cwg-icon name="copy" :size="16" color="#2e3a47" />
+                    </view>
+                </view>
+
+                <view class="divider"></view>
+                <!-- 更改交易密码按钮 -->
+                <view class="change-password-btn" @click="handleAction('changePassword1')" v-if="!isDemo">
+                    <text class="btn-icon">
+                        <cwg-icon name="crm-xg" :size="16" color="#2e3a47" />
+                    </text>
+                    <text v-t="'vu.item3'" />
+                </view>
+            </view>
+        </view>
+
+        <!-- 通知区域(预留) -->
+        <view class="notificators"></view>
+        <TerminalDialog v-model:visible="terminalDialogVisible" />
+        <TerminalChangePasswordDialog v-model:visible="terminalChangePasswordDialogVisible" :pwdType="pwdType"
+            :account="account" :accountLabel="t('Documentary.tradingCenter.item29') + ' # '" />
+        <TerminalInfoDialog v-model:visible="terminalInfoDialogVisible" :accountNumber="accountInfo.login"
+            :form="accountInfo" :fieldList="fieldList" :title="t('Documentary.TundManagement.item29')"
+            :accountLabel="t('Documentary.tradingCenter.item29') + ' # '" />
+    </view>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, nextTick, onBeforeUnmount } from 'vue';
+import useRouter from "@/hooks/useRouter";
+const router = useRouter();
+import { useI18n } from 'vue-i18n';
+const { t } = useI18n();
+import TerminalDialog from './TerminalDialog.vue'
+import TerminalChangePasswordDialog from './TerminalChangePasswordDialog.vue'
+import TerminalInfoDialog from './TerminalInfoDialog.vue'
+
+const props = defineProps<{
+    account: Account;
+    isGridLayout?: boolean;
+}>();
+const accountInfo = ref(props.account)
+const isGridLayout = ref(props.isGridLayout || false)
+// 定义账户数据类型
+export interface Account {
+    labels: string[];          // 标签数组,如 ['真实', 'MT4', 'Standard']
+    accountNumber: string;      // 账号,如 '85319215'
+    nickName: string;           // 昵称,如 '标准账户'
+    balance: number;            // 余额数字
+    currency: string;           // 货币,如 'USD'
+    actualLeverage: string;     // 实际杠杆,如 '1:2000'
+    maxLeverage: string;        // 调整杠杆,如 '1:2000'
+    floatingPL: string;         // 浮动盈亏,如 '0.00 USD'
+    creditWithSymbol: string;         // 可用保证金,如 '0.00 USD'
+    equityWithSymbol: string;             // 净值,如 '0.00 USD'
+    platform: string;           // 平台,如 'MT4'
+    server: string;             // 服务器,如 'Exness-Real28'
+    login: string;              // 登录名,如 '85319215'
+    balanceWithSymbol: string;              // 余额,如 '85319215'
+}
+const isDemo = computed(() => accountInfo.value.listType == 'demo')
+const closeFunctionOpen = (code) => {
+    const closeFunctions = accountInfo.value.closeFunctions || ""
+
+    if (closeFunctions == null || closeFunctions === "") {
+        return true;
+    }
+    return String(closeFunctions).indexOf(String(code)) === -1;
+}
+// 圆形按钮数据
+const circleButtons = ref([
+    { key: 'trade', label: 'Shop.Index.Transaction', icon: 'crm-trade', action: 'trade', needDemo: false, primary: true, disabled: false, color: '#fff' },
+    { key: 'deposit', label: 'Home.page_customer.item2', icon: 'crm-deposit', action: 'deposit', needDemo: isDemo.value, disabled: !closeFunctionOpen('1'), color: '#2e3a47' },
+    { key: 'withdraw', label: 'Home.page_customer.item3', icon: 'crm-withdraw', action: 'withdraw', needDemo: isDemo.value, disabled: !closeFunctionOpen('2'), color: '#2e3a47' },
+    { key: 'transfer', label: 'Custom.Index.Transfer', icon: 'crm-transfer', action: 'transfer', needDemo: isDemo.value, disabled: !(closeFunctionOpen('5') && closeFunctionOpen('6') && closeFunctionOpen('3')), color: '#2e3a47' }
+])
+
+// 普通按钮数据
+const actionButtons = computed(() => [
+    { key: 'trade', label: 'Shop.Index.Transaction', icon: 'crm-trade', color: '#fff', primary: true, action: 'trade', needDemo: false, disabled: false },
+    { key: 'deposit', label: 'Home.page_customer.item2', icon: 'crm-deposit', color: '#2e3a47', primary: false, action: 'deposit', needDemo: isDemo.value, disabled: !closeFunctionOpen('1') },
+    { key: 'withdraw', label: 'Home.page_customer.item3', icon: 'crm-withdraw', color: '#2e3a47', primary: false, action: 'withdraw', needDemo: isDemo.value, disabled: !closeFunctionOpen('2') },
+    { key: 'transfer', label: 'Custom.Index.Transfer', icon: 'crm-transfer', color: '#2e3a47', primary: false, action: 'transfer', needDemo: isDemo.value, disabled: !(closeFunctionOpen('5') && closeFunctionOpen('6') && closeFunctionOpen('3')) }
+])
+const fieldList = ref([
+    { label: t('Custom.PaymentHistory.AccountType'), key: 'nickname', copyable: false },
+    { label: t('Label.Leverage'), key: 'actualLeverage', copyable: false },
+    { label: t('Label.FloatingPL'), key: 'floatingPL', copyable: false },
+    { label: t('Label.Balance'), key: 'balanceWithSymbol', copyable: false },
+    { label: t('Label.Equity'), key: 'equityWithSymbol', copyable: false },
+    { label: t('Label.Credit'), key: 'creditWithSymbol', copyable: false },
+    { label: t('Documentary.console.item3'), key: 'platform', copyable: false },
+    { label: t('Documentary.console.item4'), key: 'login', copyable: true }
+])
+const nickName = ref(accountInfo.value.nickName)
+
+const infoBottomRef = ref(null);
+const infoBottomHeight = ref(0);
+// 测量高度
+const updateSubmenuHeight = () => {
+    const el = infoBottomRef.value.$el;
+    if (el) {
+        // 获取内容实际高度(可能需要暂时移除过渡影响)
+        const height = el.scrollHeight || el.offsetHeight;
+        if (height > 0) {
+            infoBottomHeight.value = height;
+        }
+    }
+};
+
+// 折叠状态
+const isExpanded = ref(false);
+
+// 切换折叠
+const toggleExpand = () => {
+    isExpanded.value = !isExpanded.value;
+    if (isExpanded.value) {
+        nextTick(() => {
+            updateSubmenuHeight();
+        });
+    }
+};
+
+
+const terminalDialogVisible = ref(false)
+const terminalChangePasswordDialogVisible = ref(false)
+const terminalInfoDialogVisible = ref(false)
+const pwdType = ref(1)
+const handleAction1 = (item) => {
+    if (item.disabled) {
+        uni.showToast({
+            title: t('news_add_field.Des.item1'),
+            icon: 'none'
+        })
+        return
+    }
+    handleAction(item.action, item)
+}
+// 处理按钮操作
+const handleAction = (type: string) => {
+    console.log(type == 'info', type, 'info');
+    switch (type) {
+        case 'trade':
+            terminalDialogVisible.value = true
+            break;
+        case 'changePassword1':
+            pwdType.value = 1
+            terminalChangePasswordDialogVisible.value = true
+            break;
+        case 'changePassword2':
+            pwdType.value = 2
+            terminalChangePasswordDialogVisible.value = true
+            break;
+        case 'info':
+            console.log(type);
+            terminalInfoDialogVisible.value = true
+            break;
+        case 'deposit':
+            toDeposit()
+            break;
+        case 'withdraw':
+            toWithdraw()
+            break;
+        case 'transfer':
+            toTransfer()
+            break;
+        default:
+            break;
+    }
+};
+
+const customMenuList = computed(() => !isDemo.value ? [{ label: t('vu.item3'), type: 'changePassword1' }, { label: t('vu.item4'), type: 'changePassword2' }, { label: t('Documentary.TundManagement.item29'), type: 'info' }] : [{ label: t('Documentary.TundManagement.item29'), type: 'info' }])
+const handleCustomClick = (item, index) => {
+    handleAction(item.value.type)
+}
+// 复制文本
+const copy = (text: string) => {
+    uni.setClipboardData({ data: text });
+};
+// 按钮对应的操作方法
+const toDeposit = () => {
+    router.push(`/pages/customer/deposit?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
+}
+const toWithdraw = () => {
+    router.push(`/pages/customer/withdrawal?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
+}
+const toTransfer = () => {
+    router.push(`/pages/customer/transfer?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
+}
+// 格式化余额,拆分为整数和小数部分
+const balanceInteger = computed(() => {
+    return Math.floor(accountInfo.value.balance).toString();
+});
+const balanceDecimal = computed(() => {
+    const parts = accountInfo.value.balance.toFixed(2).split('.');
+    return parts[1] ? '.' + parts[1] : '.00';
+});
+// 在组件挂载后初始化高度
+onMounted(() => {
+    nextTick(() => {
+        updateSubmenuHeight();
+    });
+    window.addEventListener('resize', handleResize);
+    isExpanded.value = accountInfo.value.isExpanded;
+});
+// 监听窗口 resize
+const handleResize = () => {
+    if (isExpanded.value) {
+        updateSubmenuHeight();
+    }
+};
+onBeforeUnmount(() => {
+    window.removeEventListener('resize', handleResize);
+});
+</script>
+
+<style scoped lang="scss">
+@import '@/uni.scss';
+
+.account-card {
+    border-radius: px2rpx(16);
+    padding: px2rpx(16);
+    margin-bottom: px2rpx(16);
+    position: relative;
+    border: 1px solid rgba(108, 133, 149, 0.12);
+    color: #2e3a47;
+
+    .collapse-btn {
+        position: absolute;
+        top: px2rpx(12);
+        right: px2rpx(12);
+        background: transparent;
+        border: none;
+        padding: 0;
+        cursor: pointer;
+        color: #6c8595;
+        width: px2rpx(32);
+        height: px2rpx(32);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+
+        .chevron-icon {
+            transform: rotate(90deg);
+            transition: transform 0.3s;
+
+            &.expanded {
+                transform: rotate(270deg);
+            }
+        }
+    }
+
+    .labels-container {
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        gap: px2rpx(12);
+
+        margin-bottom: px2rpx(16);
+        padding-right: px2rpx(40); // 为折叠按钮留出空间
+
+        .labels {
+            display: flex;
+            gap: px2rpx(8);
+        }
+
+        .label-badge {
+            background-color: rgba(108, 133, 149, 0.08);
+            border-radius: px2rpx(4);
+            padding: px2rpx(4) px2rpx(8);
+            font-size: px2rpx(12);
+            color: #6c8595;
+            font-weight: 500;
+        }
+
+        .account-number {
+            font-size: px2rpx(18);
+            font-weight: 600;
+            line-height: 1.3;
+        }
+
+        .account-nickname {
+            font-size: px2rpx(14);
+            color: #6c8595;
+        }
+    }
+
+    .main-content {
+        .balance {
+            margin-bottom: px2rpx(16);
+
+            .balance-number {
+                font-size: px2rpx(32);
+                font-weight: 700;
+            }
+
+            .balance-decimal {
+                font-size: px2rpx(16);
+                color: #6c8595;
+                margin-left: px2rpx(4);
+            }
+        }
+
+        // 移动端按钮组(默认显示)
+        .mobile-buttons {
+            display: flex;
+            justify-content: center;
+            gap: px2rpx(0);
+            margin-bottom: px2rpx(16);
+            flex-wrap: wrap;
+
+            .circle-btn {
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                cursor: pointer;
+                width: px2rpx(64);
+
+                .circle-icon {
+                    width: px2rpx(48);
+                    height: px2rpx(48);
+                    border-radius: 50%;
+                    background-color: #f5f7f9;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    color: #2e3a47;
+                    transition: background-color 0.2s;
+
+                    &.primary {
+                        background-color: var(--color-navy-700);
+                        color: #fff;
+                    }
+
+                    &:hover {
+                        background-color: #e9ecef;
+                    }
+
+                    svg {
+                        width: px2rpx(24);
+                        height: px2rpx(24);
+                    }
+                }
+
+                .circle-label {
+                    font-size: px2rpx(12);
+                    margin-top: px2rpx(4);
+                    color: #6c8595;
+                }
+            }
+        }
+
+        // 桌面端按钮组(默认隐藏,屏幕>1100px时显示)
+        .desktop-buttons {
+            display: none;
+
+            .action-btn {
+                background: transparent;
+                border: 1px solid rgba(108, 133, 149, 0);
+                border-radius: px2rpx(8);
+                padding: px2rpx(8) px2rpx(20);
+                font-size: px2rpx(14);
+                color: #2e3a47;
+                display: inline-flex;
+                align-items: center;
+                justify-content: center;
+                gap: px2rpx(8);
+                cursor: pointer;
+                transition: all 0.2s;
+                height: px2rpx(40);
+                box-sizing: border-box;
+                background-color: rgba(108, 133, 149, 0.08);
+
+                &.primary {
+                    background-color: var(--color-navy-700);
+                    color: #fff;
+
+
+                    &:hover {
+                        background-color: var(--color-navy-600);
+                    }
+                }
+
+                &:hover {
+                    border: 1px solid rgba(108, 133, 149, 0.2);
+                }
+
+                .btn-icon {
+                    display: flex;
+                    align-items: center;
+
+                    svg {
+                        width: px2rpx(18);
+                        height: px2rpx(18);
+                    }
+                }
+
+                &.icon-only {
+                    padding: px2rpx(8);
+                }
+            }
+        }
+
+        .is-disabled {
+            cursor: not-allowed;
+            opacity: 0.5;
+        }
+
+
+    }
+
+    .info-section {
+        display: flex;
+        gap: px2rpx(24);
+        padding: px2rpx(16) 0;
+        border-top: 1px solid rgba(108, 133, 149, 0.12);
+        border-bottom: 1px solid rgba(108, 133, 149, 0.12);
+        margin: px2rpx(16) 0;
+
+        .info-column {
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+            gap: px2rpx(12);
+        }
+
+        .info-item {
+            display: flex;
+            justify-content: space-between;
+            align-items: flex-end;
+            font-size: px2rpx(14);
+
+            .label {
+                color: #6c8595;
+            }
+
+            .line {
+                flex: 1;
+                height: 1px;
+                border-top: 1px dashed rgba(108, 133, 149, 0.5);
+                margin-bottom: px2rpx(1);
+
+            }
+
+            .value {
+                font-weight: 500;
+                color: #2e3a47;
+            }
+        }
+    }
+
+    @media screen and (max-width: 768px) {
+        .info-section {
+            flex-direction: column;
+            gap: px2rpx(12);
+        }
+    }
+
+    .extra-actions {
+        display: flex;
+        align-items: center;
+        gap: px2rpx(8);
+        margin-bottom: px2rpx(12);
+
+        .copy-row {
+            display: flex;
+            align-items: center;
+            gap: px2rpx(4);
+
+            font-size: px2rpx(14);
+
+            .label {
+                color: #6c8595;
+                min-width: px2rpx(30);
+            }
+
+            .value {
+                flex: 1;
+                color: #2e3a47;
+                font-family: monospace;
+            }
+
+            .copy-btn {
+                background: transparent;
+                border: 1px solid rgba(108, 133, 149, 0);
+                padding: 0;
+                cursor: pointer;
+                color: #6c8595;
+                width: px2rpx(24);
+                height: px2rpx(24);
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                box-sizing: border-box;
+
+                svg {
+                    width: px2rpx(20);
+                    height: px2rpx(20);
+                }
+
+                &:hover {
+                    background-color: rgba(108, 133, 149, 0.05);
+                    border: 1px solid rgba(108, 133, 149, 0.2);
+                }
+            }
+        }
+
+        .divider {
+            width: 1px;
+            height: px2rpx(20);
+            border-left: 1px solid rgba(108, 133, 149, 0.5);
+        }
+
+        .change-password-btn {
+            background: transparent;
+            box-sizing: border-box;
+            border: 1px solid rgba(108, 133, 149, 0);
+            border-radius: px2rpx(8);
+            padding: px2rpx(8) px2rpx(16);
+            font-size: px2rpx(14);
+            color: #2e3a47;
+            display: inline-flex;
+            gap: px2rpx(8);
+            cursor: pointer;
+            margin: 0;
+            // transition: all 0.2s;
+
+            .btn-icon svg {
+                width: px2rpx(16);
+                height: px2rpx(16);
+            }
+
+            &:hover {
+                background-color: rgba(108, 133, 149, 0.05);
+                border: 1px solid rgba(108, 133, 149, 0.2);
+            }
+        }
+    }
+
+    .info-bottom {
+        overflow: hidden;
+    }
+
+    .notificators {
+        // 预留通知区域
+    }
+}
+
+// 响应式:屏幕宽度 > 1100px 时,隐藏移动端按钮,显示桌面端按钮
+@media screen and (min-width: 1100px) {
+    .account-card {
+        .main-content {
+            .mobile-buttons {
+                display: none;
+            }
+
+            .desktop-buttons {
+                display: flex;
+                gap: px2rpx(8);
+                justify-content: flex-end;
+            }
+        }
+    }
+}
+
+// 网格布局时,使用移动端样式
+.account-card.is-grid-layout {
+    .main-content {
+        .mobile-buttons {
+            display: flex;
+        }
+
+        .desktop-buttons {
+            display: none;
+        }
+    }
+}
+</style>

+ 15 - 643
pages/customer/components/AccountCard.vue

@@ -1,659 +1,31 @@
 <template>
-    <view class="account-card">
-        <!-- 折叠/展开按钮 -->
-        <button class="collapse-btn" @click="toggleExpand">
-            <cwg-icon name="chevron-right" :size="20" color="#6c8595"
-                :class="['chevron-icon', { expanded: isExpanded }]" />
-        </button>
-
-        <!-- 标签区域 -->
-        <view class="labels-container">
-            <view class="labels">
-                <template v-for="(label, index) in account.labels" :key="index">
-                    <view v-if="label" class="label-badge">
-                        {{ label }}
-                    </view>
-                </template>
-            </view>
-            <view class="account-number"># {{ account.accountNumber }}</view>
-            <view class="account-nickname">{{ nickName }}</view>
-        </view>
-
-        <!-- 主要内容区域(余额和操作按钮) -->
-        <view class="main-content">
-            <!-- 余额 -->
-            <view class="balance">
-                <text class="balance-number">{{ balanceInteger }}</text>
-                <text class="balance-decimal">{{ balanceDecimal }} {{ account.currency }}</text>
-            </view>
-
-            <!-- 移动端圆形按钮组(≤1100px显示) -->
-            <view class="mobile-buttons">
-                <!-- 交易 -->
-                <template v-for="(item, index) in circleButtons" :key="index">
-                    <view class="circle-btn" :class="{ 'is-disabled': item.disabled }" @click="handleAction1(item)"
-                        v-if="!item.needDemo">
-                        <view class="circle-icon" :class="{ 'primary': item.primary }">
-                            <cwg-icon :name="item.icon" :size="16" :color="item.color" />
-                        </view>
-                        <text class="circle-label" v-t="item.label" />
-                    </view>
-                </template>
-                <!-- 更多(三点) -->
-                <cwg-dropdown @open="onOpen" @close="onClose" :menu-list="customMenuList"
-                    @menuClick="handleCustomClick">
-                    <view class="circle-btn">
-                        <view class="circle-icon">
-                            <cwg-icon name="crm-ellipsis-vertical" :size="16" color="#2e3a47" />
-                        </view>
-                        <text class="circle-label" v-t="'vu.item7'" />
-                    </view>
-                </cwg-dropdown>
-            </view>
-
-            <!-- 桌面端按钮组(>1100px显示) -->
-            <view class="desktop-buttons">
-                <template v-for="(item, index) in actionButtons" :key="index">
-                    <view class="action-btn" :class="{ 'primary': item.primary, 'is-disabled': item.disabled }"
-                        @click="handleAction1(item)" v-if="!item.needDemo">
-                        <text class="btn-icon">
-                            <cwg-icon :name="item.icon" :size="16" :color="item.color" />
-                        </text>
-                        <text v-t="item.label" />
-                    </view>
-                </template>
-                <cwg-dropdown @open="onOpen" @close="onClose" :menu-list="customMenuList"
-                    @menuClick="handleCustomClick">
-                    <view class="action-btn icon-only">
-                        <cwg-icon name="crm-ellipsis-vertical" :size="16" color="#2e3a47" />
-                    </view>
-                </cwg-dropdown>
-            </view>
-        </view>
-        <view ref="infoBottomRef" class="info-bottom" :style="{
-            height: isExpanded ? infoBottomHeight + 'px' : '0px',
-            transition: 'height 281ms cubic-bezier(0.4, 0, 0.2, 1)'
-        }">
-            <!-- 底部信息区域(折叠时隐藏) -->
-            <view class="info-section">
-                <view class="info-column">
-                    <cwg-label-line-value :label="t('Label.Leverage')" :value="account.actualLeverage" />
-                    <cwg-label-line-value :label="t('Label.FloatingPL')" :value="account.floatingPL" />
-                    <cwg-label-line-value :label="t('Label.Balance')" :value="account.balanceWithSymbol" />
-                </view>
-                <view class="info-column">
-                    <cwg-label-line-value :label="t('Label.Equity')" :value="account.equityWithSymbol" />
-                    <cwg-label-line-value :label="t('Label.Credit')" :value="account.creditWithSymbol" />
-                    <cwg-label-line-value :label="t('Documentary.console.item3')" :value="account.platform" />
-                </view>
-            </view>
-            <!-- 额外操作行(服务器、登录、更改密码,折叠时隐藏) -->
-            <view class="extra-actions">
-                <!-- 登录复制行 -->
-                <view class="copy-row">
-                    <text class="label">{{ account.platform }}</text>
-                    <text class="label" v-t="'signin.title'" />
-                    <text class="value">{{ account.login }}</text>
-                    <view class="copy-btn" @click="copy(account.login)">
-                        <cwg-icon name="copy" :size="16" color="#2e3a47" />
-                    </view>
-                </view>
-
-                <view class="divider"></view>
-                <!-- 更改交易密码按钮 -->
-                <view class="change-password-btn" @click="handleAction('changePassword1')" v-if="!isDemo">
-                    <text class="btn-icon">
-                        <cwg-icon name="crm-xg" :size="16" color="#2e3a47" />
-                    </text>
-                    <text v-t="'vu.item3'" />
-                </view>
-            </view>
-        </view>
-
-        <!-- 通知区域(预留) -->
-        <view class="notificators"></view>
-        <TerminalDialog v-model:visible="terminalDialogVisible" />
-        <TerminalChangePasswordDialog v-model:visible="terminalChangePasswordDialogVisible" :pwdType="pwdType"
-            :account="account" :accountLabel="t('Documentary.tradingCenter.item29') + ' # '" />
-        <TerminalInfoDialog v-model:visible="terminalInfoDialogVisible" :accountNumber="accountInfo.login"
-            :form="accountInfo" :fieldList="fieldList" :title="t('Documentary.TundManagement.item29')"
-            :accountLabel="t('Documentary.tradingCenter.item29') + ' # '" />
-    </view>
+    <AccountCardMobile v-if="isGridLayout" :account="account" />
+    <AccountCardDesktop v-else :account="account" />
 </template>
 
 <script setup lang="ts">
-import { ref, computed, onMounted, nextTick, onBeforeUnmount } from 'vue';
-import useRouter from "@/hooks/useRouter";
-const router = useRouter();
-import { useI18n } from 'vue-i18n';
-const { t } = useI18n();
-import TerminalDialog from './TerminalDialog.vue'
-import TerminalChangePasswordDialog from './TerminalChangePasswordDialog.vue'
-import TerminalInfoDialog from './TerminalInfoDialog.vue'
+import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
+import AccountCardMobile from './AccountCardMobile.vue';
+import AccountCardDesktop from './AccountCardDesktop.vue';
 
 const props = defineProps<{
-    account: Account;
+    account: any;
+    isGridLayout?: boolean;
 }>();
-const accountInfo = ref(props.account)
-// 定义账户数据类型
-export interface Account {
-    labels: string[];          // 标签数组,如 ['真实', 'MT4', 'Standard']
-    accountNumber: string;      // 账号,如 '85319215'
-    nickName: string;           // 昵称,如 '标准账户'
-    balance: number;            // 余额数字
-    currency: string;           // 货币,如 'USD'
-    actualLeverage: string;     // 实际杠杆,如 '1:2000'
-    maxLeverage: string;        // 调整杠杆,如 '1:2000'
-    floatingPL: string;         // 浮动盈亏,如 '0.00 USD'
-    creditWithSymbol: string;         // 可用保证金,如 '0.00 USD'
-    equityWithSymbol: string;             // 净值,如 '0.00 USD'
-    platform: string;           // 平台,如 'MT4'
-    server: string;             // 服务器,如 'Exness-Real28'
-    login: string;              // 登录名,如 '85319215'
-    balanceWithSymbol: string;              // 余额,如 '85319215'
-}
-const isDemo = computed(() => accountInfo.value.listType == 'demo')
-const closeFunctionOpen = (code) => {
-    const closeFunctions = accountInfo.value.closeFunctions || ""
-
-    if (closeFunctions == null || closeFunctions === "") {
-        return true;
-    }
-    return String(closeFunctions).indexOf(String(code)) === -1;
-}
-// 圆形按钮数据
-const circleButtons = ref([
-    { key: 'trade', label: 'Shop.Index.Transaction', icon: 'crm-trade', action: 'trade', needDemo: false, primary: true, disabled: false, color: '#fff' },
-    { key: 'deposit', label: 'Home.page_customer.item2', icon: 'crm-deposit', action: 'deposit', needDemo: isDemo.value, disabled: !closeFunctionOpen('1'), color: '#2e3a47' },
-    { key: 'withdraw', label: 'Home.page_customer.item3', icon: 'crm-withdraw', action: 'withdraw', needDemo: isDemo.value, disabled: !closeFunctionOpen('2'), color: '#2e3a47' },
-    { key: 'transfer', label: 'Custom.Index.Transfer', icon: 'crm-transfer', action: 'transfer', needDemo: isDemo.value, disabled: !(closeFunctionOpen('5') && closeFunctionOpen('6') && closeFunctionOpen('3')), color: '#2e3a47' }
-])
-
-// 普通按钮数据
-const actionButtons = computed(() => [
-    { key: 'trade', label: 'Shop.Index.Transaction', icon: 'crm-trade', color: '#fff', primary: true, action: 'trade', needDemo: false, disabled: false },
-    { key: 'deposit', label: 'Home.page_customer.item2', icon: 'crm-deposit', color: '#2e3a47', primary: false, action: 'deposit', needDemo: isDemo.value, disabled: !closeFunctionOpen('1') },
-    { key: 'withdraw', label: 'Home.page_customer.item3', icon: 'crm-withdraw', color: '#2e3a47', primary: false, action: 'withdraw', needDemo: isDemo.value, disabled: !closeFunctionOpen('2') },
-    { key: 'transfer', label: 'Custom.Index.Transfer', icon: 'crm-transfer', color: '#2e3a47', primary: false, action: 'transfer', needDemo: isDemo.value, disabled: !(closeFunctionOpen('5') && closeFunctionOpen('6') && closeFunctionOpen('3')) }
-])
-const fieldList = ref([
-    { label: t('Custom.PaymentHistory.AccountType'), key: 'nickname', copyable: false },
-    { label: t('Label.Leverage'), key: 'actualLeverage', copyable: false },
-    { label: t('Label.FloatingPL'), key: 'floatingPL', copyable: false },
-    { label: t('Label.Balance'), key: 'balanceWithSymbol', copyable: false },
-    { label: t('Label.Equity'), key: 'equityWithSymbol', copyable: false },
-    { label: t('Label.Credit'), key: 'creditWithSymbol', copyable: false },
-    { label: t('Documentary.console.item3'), key: 'platform', copyable: false },
-    { label: t('Documentary.console.item4'), key: 'login', copyable: true }
-])
-const nickName = ref(accountInfo.value.nickName)
-
-const infoBottomRef = ref(null);
-const infoBottomHeight = ref(0);
-// 测量高度
-const updateSubmenuHeight = () => {
-    const el = infoBottomRef.value.$el;
-    if (el) {
-        // 获取内容实际高度(可能需要暂时移除过渡影响)
-        const height = el.scrollHeight || el.offsetHeight;
-        if (height > 0) {
-            infoBottomHeight.value = height;
-        }
-    }
-};
 
-// 折叠状态
-const isExpanded = ref(false);
+// 检测是否为移动端
+const isMobile = ref(false);
 
-// 切换折叠
-const toggleExpand = () => {
-    isExpanded.value = !isExpanded.value;
-    if (isExpanded.value) {
-        nextTick(() => {
-            updateSubmenuHeight();
-        });
-    }
+const checkIsMobile = () => {
+    isMobile.value = window.innerWidth <= 1100;
 };
 
-
-const terminalDialogVisible = ref(false)
-const terminalChangePasswordDialogVisible = ref(false)
-const terminalInfoDialogVisible = ref(false)
-const pwdType = ref(1)
-const handleAction1 = (item) => {
-    if (item.disabled) {
-        uni.showToast({
-            title: t('news_add_field.Des.item1'),
-            icon: 'none'
-        })
-        return
-    }
-    handleAction(item.action, item)
-}
-// 处理按钮操作
-const handleAction = (type: string) => {
-    console.log(type == 'info', type, 'info');
-    switch (type) {
-        case 'trade':
-            terminalDialogVisible.value = true
-            break;
-        case 'changePassword1':
-            pwdType.value = 1
-            terminalChangePasswordDialogVisible.value = true
-            break;
-        case 'changePassword2':
-            pwdType.value = 2
-            terminalChangePasswordDialogVisible.value = true
-            break;
-        case 'info':
-            console.log(type);
-            terminalInfoDialogVisible.value = true
-            break;
-        case 'deposit':
-            toDeposit()
-            break;
-        case 'withdraw':
-            toWithdraw()
-            break;
-        case 'transfer':
-            toTransfer()
-            break;
-        default:
-            break;
-    }
-};
-
-const customMenuList = computed(() => !isDemo.value ? [{ label: t('vu.item3'), type: 'changePassword1' }, { label: t('vu.item4'), type: 'changePassword2' }, { label: t('Documentary.TundManagement.item29'), type: 'info' }] : [{ label: t('Documentary.TundManagement.item29'), type: 'info' }])
-const handleCustomClick = (item, index) => {
-    handleAction(item.value.type)
-}
-// 复制文本
-const copy = (text: string) => {
-    uni.setClipboardData({ data: text });
-};
-// 按钮对应的操作方法
-const toDeposit = () => {
-    router.push(`/pages/customer/deposit?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
-}
-const toWithdraw = () => {
-    router.push(`/pages/customer/withdrawal?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
-}
-const toTransfer = () => {
-    router.push(`/pages/customer/transfer?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
-}
-// 格式化余额,拆分为整数和小数部分
-const balanceInteger = computed(() => {
-    return Math.floor(accountInfo.value.balance).toString();
-});
-const balanceDecimal = computed(() => {
-    const parts = accountInfo.value.balance.toFixed(2).split('.');
-    return parts[1] ? '.' + parts[1] : '.00';
-});
-// 在组件挂载后初始化高度
 onMounted(() => {
-    nextTick(() => {
-        updateSubmenuHeight();
-    });
-    window.addEventListener('resize', handleResize);
-    isExpanded.value = accountInfo.value.isExpanded;
+    checkIsMobile();
+    window.addEventListener('resize', checkIsMobile);
 });
-// 监听窗口 resize
-const handleResize = () => {
-    if (isExpanded.value) {
-        updateSubmenuHeight();
-    }
-};
+
 onBeforeUnmount(() => {
-    window.removeEventListener('resize', handleResize);
+    window.removeEventListener('resize', checkIsMobile);
 });
 </script>
-
-<style scoped lang="scss">
-@import '@/uni.scss';
-
-.account-card {
-    border-radius: px2rpx(16);
-    padding: px2rpx(16);
-    margin-bottom: px2rpx(16);
-    position: relative;
-    border: 1px solid rgba(108, 133, 149, 0.12);
-    color: #2e3a47;
-
-    .collapse-btn {
-        position: absolute;
-        top: px2rpx(12);
-        right: px2rpx(12);
-        background: transparent;
-        border: none;
-        padding: 0;
-        cursor: pointer;
-        color: #6c8595;
-        width: px2rpx(32);
-        height: px2rpx(32);
-        display: flex;
-        align-items: center;
-        justify-content: center;
-
-        .chevron-icon {
-            transform: rotate(90deg);
-            transition: transform 0.3s;
-
-            &.expanded {
-                transform: rotate(270deg);
-            }
-        }
-    }
-
-    .labels-container {
-        display: flex;
-        align-items: center;
-        flex-wrap: wrap;
-        gap: px2rpx(8);
-
-        margin-bottom: px2rpx(16);
-        padding-right: px2rpx(40); // 为折叠按钮留出空间
-
-        .labels {
-            display: flex;
-            gap: px2rpx(8);
-        }
-
-        .label-badge {
-            background-color: rgba(108, 133, 149, 0.08);
-            border-radius: px2rpx(4);
-            padding: px2rpx(4) px2rpx(8);
-            font-size: px2rpx(12);
-            color: #6c8595;
-            font-weight: 500;
-        }
-
-        .account-number {
-            font-size: px2rpx(18);
-            font-weight: 600;
-            line-height: 1.3;
-        }
-
-        .account-nickname {
-            font-size: px2rpx(14);
-            color: #6c8595;
-        }
-    }
-
-    .main-content {
-        .balance {
-            margin-bottom: px2rpx(16);
-
-            .balance-number {
-                font-size: px2rpx(32);
-                font-weight: 700;
-            }
-
-            .balance-decimal {
-                font-size: px2rpx(16);
-                color: #6c8595;
-                margin-left: px2rpx(4);
-            }
-        }
-
-        // 移动端按钮组(默认显示)
-        .mobile-buttons {
-            display: flex;
-            justify-content: center;
-            gap: px2rpx(0);
-            margin-bottom: px2rpx(16);
-            flex-wrap: wrap;
-
-            .circle-btn {
-                display: flex;
-                flex-direction: column;
-                align-items: center;
-                cursor: pointer;
-                width: px2rpx(64);
-
-                .circle-icon {
-                    width: px2rpx(48);
-                    height: px2rpx(48);
-                    border-radius: 50%;
-                    background-color: #f5f7f9;
-                    display: flex;
-                    align-items: center;
-                    justify-content: center;
-                    color: #2e3a47;
-                    transition: background-color 0.2s;
-
-                    &.primary {
-                        background-color: var(--color-navy-700);
-                        color: #fff;
-                    }
-
-                    &:hover {
-                        background-color: #e9ecef;
-                    }
-
-                    svg {
-                        width: px2rpx(24);
-                        height: px2rpx(24);
-                    }
-                }
-
-                .circle-label {
-                    font-size: px2rpx(12);
-                    margin-top: px2rpx(4);
-                    color: #6c8595;
-                }
-            }
-        }
-
-        // 桌面端按钮组(默认隐藏,屏幕>1100px时显示)
-        .desktop-buttons {
-            display: none;
-
-            .action-btn {
-                background: transparent;
-                border: 1px solid rgba(108, 133, 149, 0);
-                border-radius: px2rpx(8);
-                padding: px2rpx(8) px2rpx(20);
-                font-size: px2rpx(14);
-                color: #2e3a47;
-                display: inline-flex;
-                align-items: center;
-                justify-content: center;
-                gap: px2rpx(8);
-                cursor: pointer;
-                transition: all 0.2s;
-                height: px2rpx(40);
-                box-sizing: border-box;
-                background-color: rgba(108, 133, 149, 0.08);
-
-                &.primary {
-                    background-color: var(--color-navy-700);
-                    color: #fff;
-
-
-                    &:hover {
-                        background-color: var(--color-navy-600);
-                    }
-                }
-
-                &:hover {
-                    border: 1px solid rgba(108, 133, 149, 0.2);
-                }
-
-                .btn-icon {
-                    display: flex;
-                    align-items: center;
-
-                    svg {
-                        width: px2rpx(18);
-                        height: px2rpx(18);
-                    }
-                }
-
-                &.icon-only {
-                    padding: px2rpx(8);
-                }
-            }
-        }
-
-        .is-disabled {
-            cursor: not-allowed;
-            opacity: 0.5;
-        }
-
-
-    }
-
-    .info-section {
-        display: flex;
-        gap: px2rpx(24);
-        padding: px2rpx(16) 0;
-        border-top: 1px solid rgba(108, 133, 149, 0.12);
-        border-bottom: 1px solid rgba(108, 133, 149, 0.12);
-        margin: px2rpx(16) 0;
-
-        .info-column {
-            flex: 1;
-            display: flex;
-            flex-direction: column;
-            gap: px2rpx(12);
-        }
-
-        .info-item {
-            display: flex;
-            justify-content: space-between;
-            align-items: flex-end;
-            font-size: px2rpx(14);
-
-            .label {
-                color: #6c8595;
-            }
-
-            .line {
-                flex: 1;
-                height: 1px;
-                border-top: 1px dashed rgba(108, 133, 149, 0.5);
-                margin-bottom: px2rpx(1);
-
-            }
-
-            .value {
-                font-weight: 500;
-                color: #2e3a47;
-            }
-        }
-    }
-
-    @media screen and (max-width: 768px) {
-        .info-section {
-            flex-direction: column;
-            gap: px2rpx(12);
-        }
-    }
-
-    .extra-actions {
-        display: flex;
-        align-items: center;
-        gap: px2rpx(8);
-        margin-bottom: px2rpx(12);
-
-        .copy-row {
-            display: flex;
-            align-items: center;
-            gap: px2rpx(8);
-
-            font-size: px2rpx(14);
-
-            .label {
-                color: #6c8595;
-                min-width: px2rpx(30);
-            }
-
-            .value {
-                flex: 1;
-                color: #2e3a47;
-                font-family: monospace;
-            }
-
-            .copy-btn {
-                background: transparent;
-                border: 1px solid rgba(108, 133, 149, 0);
-                padding: 0;
-                cursor: pointer;
-                color: #6c8595;
-                width: px2rpx(32);
-                height: px2rpx(32);
-                display: flex;
-                align-items: center;
-                justify-content: center;
-                box-sizing: border-box;
-
-                svg {
-                    width: px2rpx(20);
-                    height: px2rpx(20);
-                }
-
-                &:hover {
-                    background-color: rgba(108, 133, 149, 0.05);
-                    border: 1px solid rgba(108, 133, 149, 0.2);
-                }
-            }
-        }
-
-        .divider {
-            width: 1px;
-            height: px2rpx(20);
-            border-left: 1px solid rgba(108, 133, 149, 0.5);
-        }
-
-        .change-password-btn {
-            background: transparent;
-            box-sizing: border-box;
-            border: 1px solid rgba(108, 133, 149, 0);
-            border-radius: px2rpx(8);
-            padding: px2rpx(8) px2rpx(16);
-            font-size: px2rpx(14);
-            color: #2e3a47;
-            display: inline-flex;
-            gap: px2rpx(8);
-            cursor: pointer;
-            margin: 0;
-            // transition: all 0.2s;
-
-            .btn-icon svg {
-                width: px2rpx(16);
-                height: px2rpx(16);
-            }
-
-            &:hover {
-                background-color: rgba(108, 133, 149, 0.05);
-                border: 1px solid rgba(108, 133, 149, 0.2);
-            }
-        }
-    }
-
-    .info-bottom {
-        overflow: hidden;
-    }
-
-    .notificators {
-        // 预留通知区域
-    }
-}
-
-// 响应式:屏幕宽度 > 1100px 时,隐藏移动端按钮,显示桌面端按钮
-@media screen and (min-width: 1100px) {
-    .account-card {
-        .main-content {
-            .mobile-buttons {
-                display: none;
-            }
-
-            .desktop-buttons {
-                display: flex;
-                gap: px2rpx(8);
-                justify-content: flex-end;
-            }
-        }
-    }
-}
-</style>

+ 682 - 0
pages/customer/components/AccountCardDesktop.vue

@@ -0,0 +1,682 @@
+<template>
+    <view class="account-card">
+        <!-- 折叠/展开按钮 -->
+        <button class="collapse-btn" @click="toggleExpand">
+            <cwg-icon name="chevron-right" :size="20" color="#6c8595"
+                :class="['chevron-icon', { expanded: isExpanded }]" />
+        </button>
+
+        <!-- 标签区域 -->
+        <view class="labels-container">
+            <view class="labels">
+                <template v-for="(label, index) in account.labels" :key="index">
+                    <view v-if="label" class="label-badge">
+                        {{ label }}
+                    </view>
+                </template>
+            </view>
+            <view class="account-number"># {{ account.accountNumber }}</view>
+            <view class="account-nickname">{{ nickName }}</view>
+        </view>
+
+        <!-- 主要内容区域(余额和操作按钮) -->
+        <view class="main-content">
+            <!-- 余额 -->
+            <view class="balance">
+                <text class="balance-number">{{ balanceInteger }}</text>
+                <text class="balance-decimal">{{ balanceDecimal }} {{ account.currency }}</text>
+            </view>
+
+            <!-- 移动端圆形按钮组(≤1100px显示) -->
+            <view class="mobile-buttons">
+                <!-- 交易 -->
+                <template v-for="(item, index) in circleButtons" :key="index">
+                    <view class="circle-btn" :class="{ 'is-disabled': item.disabled }" @click="handleAction1(item)"
+                        v-if="!item.needDemo">
+                        <view class="circle-icon" :class="{ 'primary': item.primary }">
+                            <cwg-icon :name="item.icon" :size="16" :color="item.color" />
+                        </view>
+                        <text class="circle-label" v-t="item.label" />
+                    </view>
+                </template>
+                <!-- 更多(三点) -->
+                <cwg-dropdown @open="onOpen" @close="onClose" :menu-list="customMenuList"
+                    @menuClick="handleCustomClick">
+                    <view class="circle-btn">
+                        <view class="circle-icon">
+                            <cwg-icon name="crm-ellipsis-vertical" :size="16" color="#2e3a47" />
+                        </view>
+                        <text class="circle-label" v-t="'Latest.More'" />
+                    </view>
+                </cwg-dropdown>
+            </view>
+
+            <!-- 桌面端按钮组(>1100px显示) -->
+            <view class="desktop-buttons">
+                <template v-for="(item, index) in actionButtons" :key="index">
+                    <view class="action-btn" :class="{ 'primary': item.primary, 'is-disabled': item.disabled }"
+                        @click="handleAction1(item)" v-if="!item.needDemo">
+                        <span class="btn-icon">
+                            <cwg-icon :name="item.icon" :size="16" :color="item.color" />
+                        </span>
+                        <text v-t="item.label" />
+                    </view>
+                </template>
+                <cwg-dropdown @open="onOpen" @close="onClose" :menu-list="customMenuList"
+                    @menuClick="handleCustomClick">
+                    <view class="action-btn icon-only">
+                        <cwg-icon name="crm-ellipsis-vertical" :size="16" color="#2e3a47" />
+                    </view>
+                </cwg-dropdown>
+            </view>
+        </view>
+        <view ref="infoBottomRef" class="info-bottom" :style="{
+            height: isExpanded ? infoBottomHeight + 'px' : '0px',
+            transition: 'height 281ms cubic-bezier(0.4, 0, 0.2, 1)'
+        }">
+            <!-- 底部信息区域(折叠时隐藏) -->
+            <view class="info-section">
+                <view class="info-column">
+                    <cwg-label-line-value :label="t('Label.Leverage')" :value="account.actualLeverage" />
+                    <cwg-label-line-value :label="t('Documentary.console.item5')" :value="account.floatingPL" />
+                    <cwg-label-line-value :label="t('Label.Balance')" :value="account.balanceWithSymbol" />
+                </view>
+                <view class="info-column">
+                    <cwg-label-line-value :label="t('Label.Equity')" :value="account.equityWithSymbol" />
+                    <cwg-label-line-value :label="t('Label.Credit')" :value="account.creditWithSymbol" />
+                    <cwg-label-line-value :label="t('Documentary.console.item3')" :value="account.platform" />
+                </view>
+            </view>
+            <!-- 额外操作行(服务器、登录、更改密码,折叠时隐藏) -->
+            <view class="extra-actions">
+                <!-- 登录复制行 -->
+                <view class="copy-row">
+                    <span class="label">{{ account.platform }} 登陆</span>
+                    <span class="value">{{ account.login }}</span>
+                    <view class="copy-btn" @click="copy(account.login)">
+                        <cwg-icon name="copy" :size="16" color="#2e3a47" />
+                    </view>
+                </view>
+
+                <!-- 更改交易密码按钮 -->
+                <view class="change-password-btn" @click="handleAction('changePassword1')" v-if="!isDemo">
+                    <span class="btn-icon">
+                        <cwg-icon name="crm-xg" :size="16" color="#2e3a47" />
+                    </span>
+                    更改交易密码
+                </view>
+            </view>
+        </view>
+
+        <!-- 通知区域(预留) -->
+        <view class="notificators"></view>
+        <TerminalDialog v-model:visible="terminalDialogVisible" />
+        <TerminalChangePasswordDialog v-model:visible="terminalChangePasswordDialogVisible" :pwdType="pwdType"
+            :account="account" />
+        <TerminalInfoDialog v-model:visible="terminalInfoDialogVisible" :accountNumber="accountInfo.login"
+            :form="accountInfo" :fieldList="fieldList" />
+    </view>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, nextTick, onBeforeUnmount } from 'vue';
+import useRouter from "@/hooks/useRouter";
+const router = useRouter();
+import { useI18n } from 'vue-i18n';
+const { t } = useI18n();
+import TerminalDialog from './TerminalDialog.vue'
+import TerminalChangePasswordDialog from './TerminalChangePasswordDialog.vue'
+import TerminalInfoDialog from './TerminalInfoDialog.vue'
+
+const props = defineProps<{
+    account: Account;
+}>();
+const accountInfo = ref(props.account)
+// 定义账户数据类型
+export interface Account {
+    labels: string[];          // 标签数组,如 ['真实', 'MT4', 'Standard']
+    accountNumber: string;      // 账号,如 '85319215'
+    nickName: string;           // 昵称,如 '标准账户'
+    balance: number;            // 余额数字
+    currency: string;           // 货币,如 'USD'
+    actualLeverage: string;     // 实际杠杆,如 '1:2000'
+    maxLeverage: string;        // 调整杠杆,如 '1:2000'
+    floatingPL: string;         // 浮动盈亏,如 '0.00 USD'
+    creditWithSymbol: string;         // 可用保证金,如 '0.00 USD'
+    equityWithSymbol: string;             // 净值,如 '0.00 USD'
+    platform: string;           // 平台,如 'MT4'
+    server: string;             // 服务器,如 'Exness-Real28'
+    login: string;              // 登录名,如 '85319215'
+    balanceWithSymbol: string;              // 余额,如 '85319215'
+}
+const isDemo = computed(() => accountInfo.value.listType == 'demo')
+const closeFunctionOpen = (code) => {
+    console.log(code, 121212);
+    const closeFunctions = accountInfo.value.closeFunctions || ""
+
+    if (closeFunctions == null || closeFunctions === "") {
+        return true;
+    }
+    return String(closeFunctions).indexOf(String(code)) === -1;
+}
+// 圆形按钮数据
+const circleButtons = ref([
+    { key: 'trade', label: 'Shop.Index.Transaction', icon: 'crm-trade', action: 'trade', needDemo: false, primary: true, disabled: false, color: '#fff' },
+    { key: 'deposit', label: 'Home.page_customer.item2', icon: 'crm-deposit', action: 'deposit', needDemo: isDemo.value, disabled: !closeFunctionOpen('1'), color: '#2e3a47' },
+    { key: 'withdraw', label: 'Home.page_customer.item3', icon: 'crm-withdraw', action: 'withdraw', needDemo: isDemo.value, disabled: !closeFunctionOpen('2'), color: '#2e3a47' },
+    { key: 'transfer', label: 'Custom.Index.Transfer', icon: 'crm-transfer', action: 'transfer', needDemo: isDemo.value, disabled: !(closeFunctionOpen('5') && closeFunctionOpen('6') && closeFunctionOpen('3')), color: '#2e3a47' }
+])
+
+// 普通按钮数据
+const actionButtons = computed(() => [
+    { key: 'trade', label: 'Shop.Index.Transaction', icon: 'crm-trade', color: '#fff', primary: true, action: 'trade', needDemo: false, disabled: false },
+    { key: 'deposit', label: 'Home.page_customer.item2', icon: 'crm-deposit', color: '#2e3a47', primary: false, action: 'deposit', needDemo: isDemo.value, disabled: !closeFunctionOpen('1') },
+    { key: 'withdraw', label: 'Home.page_customer.item3', icon: 'crm-withdraw', color: '#2e3a47', primary: false, action: 'withdraw', needDemo: isDemo.value, disabled: !closeFunctionOpen('2') },
+    { key: 'transfer', label: 'Custom.Index.Transfer', icon: 'crm-transfer', color: '#2e3a47', primary: false, action: 'transfer', needDemo: isDemo.value, disabled: !(closeFunctionOpen('5') && closeFunctionOpen('6') && closeFunctionOpen('3')) }
+])
+const fieldList = ref([
+    { label: 'Custom.PaymentHistory.AccountType', key: 'nickname', copyable: false },
+    { label: 'Label.Leverage', key: 'actualLeverage', copyable: false },
+    { label: 'Documentary.console.item5', key: 'floatingPL', copyable: false },
+    { label: 'Label.Balance', key: 'balanceWithSymbol', copyable: false },
+    { label: 'Label.Equity', key: 'equityWithSymbol', copyable: false },
+    { label: 'Label.Credit', key: 'creditWithSymbol', copyable: false },
+    { label: 'Documentary.console.item3', key: 'platform', copyable: false },
+    { label: 'Documentary.console.item4', key: 'login', copyable: true }
+])
+const nickName = ref(accountInfo.value.nickName)
+
+const infoBottomRef = ref(null);
+const infoBottomHeight = ref(0);
+// 测量高度
+const updateSubmenuHeight = () => {
+    const el = infoBottomRef.value.$el;
+    if (el) {
+        // 获取内容实际高度(可能需要暂时移除过渡影响)
+        const height = el.scrollHeight || el.offsetHeight;
+        if (height > 0) {
+            infoBottomHeight.value = height;
+        }
+    }
+};
+
+// 折叠状态
+const isExpanded = ref(false);
+
+// 切换折叠
+const toggleExpand = () => {
+    isExpanded.value = !isExpanded.value;
+    if (isExpanded.value) {
+        nextTick(() => {
+            updateSubmenuHeight();
+        });
+    }
+};
+
+
+const terminalDialogVisible = ref(false)
+const terminalChangePasswordDialogVisible = ref(false)
+const terminalInfoDialogVisible = ref(false)
+const pwdType = ref(1)
+const handleAction1 = (item) => {
+    if (item.disabled) {
+        uni.showToast({
+            title: t('news_add_field.Des.item1'),
+            icon: 'none'
+        })
+        return
+    }
+    handleAction(item.action, item)
+}
+// 处理按钮操作
+const handleAction = (type: string) => {
+    switch (type) {
+        case 'trade':
+            terminalDialogVisible.value = true
+            break;
+        case 'changePassword1':
+            pwdType.value = 1
+            terminalChangePasswordDialogVisible.value = true
+            break;
+        case 'changePassword2':
+            pwdType.value = 2
+            terminalChangePasswordDialogVisible.value = true
+            break;
+        case 'info':
+            console.log(type);
+            terminalInfoDialogVisible.value = true
+            break;
+        case 'deposit':
+            toDeposit()
+            break;
+        case 'withdraw':
+            toWithdraw()
+            break;
+        case 'transfer':
+            toTransfer()
+            break;
+        case 'position':
+            toPosition()
+            break;
+        case 'history':
+            toHistory()
+            break;
+        case 'payment-history':
+            toPaymentHistory()
+            break;
+        default:
+            break;
+    }
+};
+
+const customMenuList = computed(() => !isDemo.value ? [
+    { label: t('Ib.Report.Tit1'), type: 'history' },
+    { label: t('Ib.Report.Tit4'), type: 'position' },
+    { label: t('Home.page_customer.item4'), type: 'payment-history' },
+    { label: t('Documentary.TundManagement.item29'), type: 'info' },
+    { label: t('vu.item3'), type: 'changePassword1' },
+    { label: t('vu.item4'), type: 'changePassword2' }
+] : [
+    { label: t('Ib.Report.Tit1'), type: 'history' },
+    { label: t('Ib.Report.Tit4'), type: 'position' },
+    { label: t('Documentary.TundManagement.item29'), type: 'info' }
+])
+const handleCustomClick = (item, index) => {
+    handleAction(item.value.type)
+}
+// 复制文本
+const copy = (text: string) => {
+    uni.setClipboardData({ data: text });
+};
+// 按钮对应的操作方法
+const toHistory = () => {
+    router.push(`/pages/customer/trade-history?login=${accountInfo.value.login}`)
+}
+// 按钮对应的操作方法
+const toPosition = () => {
+    router.push(`/pages/customer/trade-position?login=${accountInfo.value.login}`)
+}
+// 按钮对应的操作方法
+const toPaymentHistory = () => {
+    router.push(`/pages/customer/payment-history?login=${accountInfo.value.login}`)
+}
+// 按钮对应的操作方法
+const toDeposit = () => {
+    router.push(`/pages/customer/deposit?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
+}
+const toWithdraw = () => {
+    router.push(`/pages/customer/withdrawal?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
+}
+const toTransfer = () => {
+    router.push(`/pages/customer/transfer?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
+}
+// 格式化余额,拆分为整数和小数部分
+const balanceInteger = computed(() => {
+    return Math.floor(accountInfo.value.balance).toString();
+});
+const balanceDecimal = computed(() => {
+    const parts = accountInfo.value.balance.toFixed(2).split('.');
+    return parts[1] ? '.' + parts[1] : '.00';
+});
+// 在组件挂载后初始化高度
+onMounted(() => {
+    nextTick(() => {
+        updateSubmenuHeight();
+    });
+    window.addEventListener('resize', handleResize);
+    isExpanded.value = accountInfo.value.isExpanded;
+});
+// 监听窗口 resize
+const handleResize = () => {
+    if (isExpanded.value) {
+        updateSubmenuHeight();
+    }
+};
+onBeforeUnmount(() => {
+    window.removeEventListener('resize', handleResize);
+});
+</script>
+
+<style scoped lang="scss">
+@import '@/uni.scss';
+
+.account-card {
+    border-radius: px2rpx(16);
+    padding: px2rpx(16);
+    margin-bottom: px2rpx(16);
+    position: relative;
+    border: 1px solid rgba(108, 133, 149, 0.12);
+    color: #2e3a47;
+
+    .collapse-btn {
+        position: absolute;
+        top: px2rpx(12);
+        right: px2rpx(12);
+        background: transparent;
+        border: none;
+        padding: 0;
+        cursor: pointer;
+        color: #6c8595;
+        width: px2rpx(32);
+        height: px2rpx(32);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+
+        .chevron-icon {
+            transform: rotate(90deg);
+            transition: transform 0.3s;
+
+            &.expanded {
+                transform: rotate(270deg);
+            }
+        }
+    }
+
+    .labels-container {
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        gap: px2rpx(8);
+
+        margin-bottom: px2rpx(16);
+        padding-right: px2rpx(40); // 为折叠按钮留出空间
+
+        .labels {
+            display: flex;
+            gap: px2rpx(8);
+        }
+
+        .label-badge {
+            background-color: rgba(108, 133, 149, 0.08);
+            border-radius: px2rpx(4);
+            padding: px2rpx(4) px2rpx(8);
+            font-size: px2rpx(12);
+            color: #6c8595;
+            font-weight: 500;
+        }
+
+        .account-number {
+            font-size: px2rpx(18);
+            font-weight: 600;
+            line-height: 1.3;
+        }
+
+        .account-nickname {
+            font-size: px2rpx(14);
+            color: #6c8595;
+        }
+    }
+
+    .main-content {
+        .balance {
+            margin-bottom: px2rpx(16);
+
+            .balance-number {
+                font-size: px2rpx(32);
+                font-weight: 700;
+            }
+
+            .balance-decimal {
+                font-size: px2rpx(16);
+                color: #6c8595;
+                margin-left: px2rpx(4);
+            }
+        }
+
+        // 移动端按钮组(默认显示)
+        .mobile-buttons {
+            display: flex;
+            justify-content: center;
+            gap: px2rpx(0);
+            margin-bottom: px2rpx(16);
+            flex-wrap: wrap;
+
+            .circle-btn {
+                display: flex;
+                flex-direction: column;
+                align-items: center;
+                cursor: pointer;
+                width: px2rpx(64);
+
+                .circle-icon {
+                    width: px2rpx(48);
+                    height: px2rpx(48);
+                    border-radius: 50%;
+                    background-color: #f5f7f9;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                    color: #2e3a47;
+                    transition: background-color 0.2s;
+
+                    &.primary {
+                        background-color: var(--color-navy-700);
+                        color: #fff;
+                    }
+
+                    &:hover {
+                        background-color: #e9ecef;
+                    }
+
+                    svg {
+                        width: px2rpx(24);
+                        height: px2rpx(24);
+                    }
+                }
+
+                .circle-label {
+                    font-size: px2rpx(12);
+                    margin-top: px2rpx(4);
+                    color: #6c8595;
+                }
+            }
+        }
+
+        // 桌面端按钮组(默认隐藏,屏幕>1100px时显示)
+        .desktop-buttons {
+            display: none;
+
+            .action-btn {
+                background: transparent;
+                border: 1px solid rgba(108, 133, 149, 0);
+                border-radius: px2rpx(8);
+                padding: px2rpx(8) px2rpx(20);
+                font-size: px2rpx(14);
+                color: #2e3a47;
+                display: inline-flex;
+                align-items: center;
+                justify-content: center;
+                gap: px2rpx(8);
+                cursor: pointer;
+                transition: all 0.2s;
+                height: px2rpx(40);
+                box-sizing: border-box;
+                background-color: rgba(108, 133, 149, 0.08);
+
+                &.primary {
+                    background-color: var(--color-navy-700);
+                    color: #fff;
+
+
+                    &:hover {
+                        background-color: var(--color-navy-600);
+                    }
+                }
+
+                &:hover {
+                    border: 1px solid rgba(108, 133, 149, 0.2);
+                }
+
+                .btn-icon {
+                    display: flex;
+                    align-items: center;
+
+                    svg {
+                        width: px2rpx(18);
+                        height: px2rpx(18);
+                    }
+                }
+
+                &.icon-only {
+                    padding: px2rpx(8);
+                }
+            }
+        }
+
+        .is-disabled {
+            cursor: not-allowed;
+            opacity: 0.5;
+        }
+
+
+    }
+
+    .info-section {
+        display: flex;
+        gap: px2rpx(24);
+        padding: px2rpx(16) 0;
+        border-top: 1px solid rgba(108, 133, 149, 0.12);
+        border-bottom: 1px solid rgba(108, 133, 149, 0.12);
+        margin: px2rpx(16) 0;
+
+        .info-column {
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+            gap: px2rpx(12);
+        }
+
+        .info-item {
+            display: flex;
+            justify-content: space-between;
+            align-items: flex-end;
+            font-size: px2rpx(14);
+
+            .label {
+                color: #6c8595;
+            }
+
+            .line {
+                flex: 1;
+                height: 1px;
+                border-top: 1px dashed rgba(108, 133, 149, 0.5);
+                margin-bottom: px2rpx(1);
+
+            }
+
+            .value {
+                font-weight: 500;
+                color: #2e3a47;
+            }
+        }
+    }
+
+    @media screen and (max-width: 768px) {
+        .info-section {
+            flex-direction: column;
+            gap: px2rpx(12);
+        }
+    }
+
+    .extra-actions {
+        display: flex;
+        align-items: center;
+        gap: px2rpx(8);
+        margin-bottom: px2rpx(12);
+
+        .copy-row {
+            display: flex;
+            align-items: center;
+            gap: px2rpx(8);
+
+            font-size: px2rpx(14);
+
+            .label {
+                color: #6c8595;
+                min-width: px2rpx(70);
+            }
+
+            .value {
+                flex: 1;
+                color: #2e3a47;
+                font-family: monospace;
+            }
+
+            .copy-btn {
+                background: transparent;
+                border: 1px solid rgba(108, 133, 149, 0);
+                padding: 0;
+                cursor: pointer;
+                color: #6c8595;
+                width: px2rpx(32);
+                height: px2rpx(32);
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                box-sizing: border-box;
+
+                svg {
+                    width: px2rpx(20);
+                    height: px2rpx(20);
+                }
+
+                &:hover {
+                    background-color: rgba(108, 133, 149, 0.05);
+                    border: 1px solid rgba(108, 133, 149, 0.2);
+                }
+            }
+        }
+
+        .change-password-btn {
+            background: transparent;
+            box-sizing: border-box;
+            border: 1px solid rgba(108, 133, 149, 0);
+            border-radius: px2rpx(8);
+            padding: px2rpx(8) px2rpx(16);
+            font-size: px2rpx(14);
+            color: #2e3a47;
+            display: inline-flex;
+            gap: px2rpx(8);
+            cursor: pointer;
+            margin: 0;
+            // transition: all 0.2s;
+
+            .btn-icon svg {
+                width: px2rpx(16);
+                height: px2rpx(16);
+            }
+
+            &:hover {
+                background-color: rgba(108, 133, 149, 0.05);
+                border: 1px solid rgba(108, 133, 149, 0.2);
+            }
+        }
+    }
+
+    .info-bottom {
+        overflow: hidden;
+    }
+
+    .notificators {
+        // 预留通知区域
+    }
+}
+
+// 响应式:屏幕宽度 > 1100px 时,隐藏移动端按钮,显示桌面端按钮
+@media screen and (min-width: 1100px) {
+    .account-card {
+        .main-content {
+            .mobile-buttons {
+                display: none;
+            }
+
+            .desktop-buttons {
+                display: flex;
+                gap: px2rpx(8);
+                justify-content: flex-end;
+            }
+        }
+    }
+}
+</style>

+ 539 - 0
pages/customer/components/AccountCardMobile.vue

@@ -0,0 +1,539 @@
+<template>
+    <view class="account-card">
+        <!-- 更多按钮 -->
+
+
+        <!-- 标签区域 -->
+        <view class="card-top">
+            <view class="labels-container">
+                <view class="account-number">{{ account.accountNumber }}</view>
+                <view class="account-number" @click="copy(account.fwq)">{{ account.fwq }}</view>
+                <view class="labels">
+                    <template v-for="(label, index) in account.labels" :key="index">
+                        <view v-if="label" class="label-badge">
+                            {{ label }}
+                        </view>
+                    </template>
+                </view>
+            </view>
+            <cwg-dropdown @open="onOpen" @close="onClose" :menu-list="customMenuList" @menuClick="handleCustomClick">
+                <view class="more-icon more-btn">
+                    <cwg-icon name="crm-ellipsis-vertical" :size="20" color="#6c8595" />
+                </view>
+                <template #btn>
+                    <view class="mobile-buttons">
+                        <!-- 交易 -->
+                        <template v-for="(item, index) in circleButtons" :key="index">
+                            <view class="circle-btn" :class="{ 'is-disabled': item.disabled }"
+                                @click="handleAction1(item)" v-if="!item.needDemo">
+                                <view class="circle-icon" :class="{ 'primary': item.primary }">
+                                    <cwg-icon :name="item.icon" :size="16" :color="item.color" />
+                                </view>
+                                <text class="circle-label" v-t="item.label" />
+                            </view>
+                        </template>
+                    </view>
+                </template>
+            </cwg-dropdown>
+        </view>
+
+
+        <!-- 主要内容区域(余额和操作按钮) -->
+        <view class="main-content">
+            <!-- 余额 -->
+            <view class="balance">
+                <text class="balance-number">{{ balanceInteger }}</text>
+                <text class="balance-decimal">{{ balanceDecimal }} {{ account.currency }}</text>
+            </view>
+
+            <!-- 移动端圆形按钮组 -->
+
+        </view>
+        <view class="info-bottom">
+            <!-- 底部信息区域(折叠时隐藏) -->
+            <view class="info-section">
+                <view class="info-column">
+                    <cwg-label-line-value :label="t('Label.Leverage')" :value="account.actualLeverage" />
+                    <cwg-label-line-value :label="t('Label.FloatingPL')" :value="account.floatingPL" />
+                    <cwg-label-line-value :label="t('Label.Balance')" :value="account.balanceWithSymbol" />
+                </view>
+                <view class="info-column">
+                    <cwg-label-line-value :label="t('Label.Equity')" :value="account.equityWithSymbol" />
+                    <cwg-label-line-value :label="t('Label.Credit')" :value="account.creditWithSymbol" />
+                    <cwg-label-line-value :label="t('Documentary.console.item3')" :value="account.platform" />
+                </view>
+            </view>
+        </view>
+
+        <TerminalDialog v-model:visible="terminalDialogVisible" />
+        <TerminalChangePasswordDialog v-model:visible="terminalChangePasswordDialogVisible" :pwdType="pwdType"
+            :account="account" :accountLabel="t('Documentary.tradingCenter.item29') + ' # '" />
+        <TerminalInfoDialog v-model:visible="terminalInfoDialogVisible" :accountNumber="accountInfo.login"
+            :form="accountInfo" :fieldList="fieldList" :title="t('Documentary.TundManagement.item29')"
+            :accountLabel="t('Documentary.tradingCenter.item29') + ' # '" />
+    </view>
+</template>
+
+<script setup lang="ts">
+import { ref, computed, onMounted, nextTick, onBeforeUnmount } from 'vue';
+import useRouter from "@/hooks/useRouter";
+const router = useRouter();
+import { useI18n } from 'vue-i18n';
+const { t } = useI18n();
+import TerminalDialog from './TerminalDialog.vue'
+import TerminalChangePasswordDialog from './TerminalChangePasswordDialog.vue'
+import TerminalInfoDialog from './TerminalInfoDialog.vue'
+
+const props = defineProps<{
+    account: Account;
+}>();
+const accountInfo = ref(props.account)
+// 定义账户数据类型
+export interface Account {
+    labels: string[];          // 标签数组,如 ['真实', 'MT4', 'Standard']
+    accountNumber: string;      // 账号,如 '85319215'
+    nickName: string;           // 昵称,如 '标准账户'
+    balance: number;            // 余额数字
+    currency: string;           // 货币,如 'USD'
+    actualLeverage: string;     // 实际杠杆,如 '1:2000'
+    maxLeverage: string;        // 调整杠杆,如 '1:2000'
+    floatingPL: string;         // 浮动盈亏,如 '0.00 USD'
+    creditWithSymbol: string;         // 可用保证金,如 '0.00 USD'
+    equityWithSymbol: string;             // 净值,如 '0.00 USD'
+    platform: string;           // 平台,如 'MT4'
+    server: string;             // 服务器,如 'Exness-Real28'
+    login: string;              // 登录名,如 '85319215'
+    balanceWithSymbol: string;              // 余额,如 '85319215'
+    fwq: string;                // 服务器名称
+    listType: string;           // 账户类型,如 'real' 或 'demo'
+    closeFunctions?: string;    // 关闭的功能
+}
+const isDemo = computed(() => accountInfo.value.listType == 'demo')
+const closeFunctionOpen = (code) => {
+    const closeFunctions = accountInfo.value.closeFunctions || ""
+    if (closeFunctions == null || closeFunctions === "") {
+        return true;
+    }
+    return String(closeFunctions).indexOf(String(code)) === -1;
+}
+// 圆形按钮数据
+const circleButtons = ref([
+    { key: 'trade', label: 'Shop.Index.Transaction', icon: 'crm-trade', action: 'trade', needDemo: false, primary: true, disabled: false, color: '#fff' },
+    { key: 'deposit', label: 'Home.page_customer.item2', icon: 'crm-deposit', action: 'deposit', needDemo: isDemo.value, disabled: !closeFunctionOpen('1'), color: '#2e3a47' },
+    { key: 'withdraw', label: 'Home.page_customer.item3', icon: 'crm-withdraw', action: 'withdraw', needDemo: isDemo.value, disabled: !closeFunctionOpen('2'), color: '#2e3a47' },
+    { key: 'transfer', label: 'Custom.Index.Transfer', icon: 'crm-transfer', action: 'transfer', needDemo: isDemo.value, disabled: !(closeFunctionOpen('5') && closeFunctionOpen('6') && closeFunctionOpen('3')), color: '#2e3a47' }
+])
+
+const fieldList = ref([
+    { label: t('Custom.PaymentHistory.AccountType'), key: 'nickname', copyable: false },
+    { label: t('Label.Leverage'), key: 'actualLeverage', copyable: false },
+    { label: t('Label.FloatingPL'), key: 'floatingPL', copyable: false },
+    { label: t('Label.Balance'), key: 'balanceWithSymbol', copyable: false },
+    { label: t('Label.Equity'), key: 'equityWithSymbol', copyable: false },
+    { label: t('Label.Credit'), key: 'creditWithSymbol', copyable: false },
+    { label: t('Documentary.console.item3'), key: 'platform', copyable: false },
+    { label: t('Documentary.console.item4'), key: 'login', copyable: true }
+])
+const nickName = ref(accountInfo.value.nickName);
+
+
+const terminalDialogVisible = ref(false)
+const terminalChangePasswordDialogVisible = ref(false)
+const terminalInfoDialogVisible = ref(false)
+const pwdType = ref(1)
+const handleAction1 = (item) => {
+    if (item.disabled) {
+        uni.showToast({
+            title: t('news_add_field.Des.item1'),
+            icon: 'none'
+        })
+        return
+    }
+    handleAction(item.action, item)
+}
+// 处理按钮操作
+const handleAction = (type: string) => {
+    switch (type) {
+        case 'trade':
+            terminalDialogVisible.value = true
+            break;
+        case 'changePassword1':
+            pwdType.value = 1
+            terminalChangePasswordDialogVisible.value = true
+            break;
+        case 'changePassword2':
+            pwdType.value = 2
+            terminalChangePasswordDialogVisible.value = true
+            break;
+        case 'info':
+            terminalInfoDialogVisible.value = true
+            break;
+        case 'deposit':
+            toDeposit()
+            break;
+        case 'withdraw':
+            toWithdraw()
+            break;
+        case 'transfer':
+            toTransfer()
+            break;
+        case 'position':
+            toPosition()
+            break;
+        case 'history':
+            toHistory()
+            break;
+        case 'payment-history':
+            toPaymentHistory()
+            break;
+        default:
+            break;
+    }
+};
+
+const customMenuList = computed(() => !isDemo.value ? [
+    { label: t('Ib.Report.Tit1'), type: 'history' },
+    { label: t('Ib.Report.Tit4'), type: 'position' },
+    { label: t('Home.page_customer.item4'), type: 'payment-history' },
+    { label: t('Documentary.TundManagement.item29'), type: 'info' },
+    { label: t('vu.item3'), type: 'changePassword1' },
+    { label: t('vu.item4'), type: 'changePassword2' }
+] : [
+    { label: t('Ib.Report.Tit1'), type: 'history' },
+    { label: t('Ib.Report.Tit4'), type: 'position' },
+    { label: t('Documentary.TundManagement.item29'), type: 'info' }
+])
+const handleCustomClick = (item, index) => {
+    handleAction(item.value.type)
+}
+// 复制文本
+const copy = (text: string) => {
+    uni.setClipboardData({ data: text });
+};
+// 按钮对应的操作方法
+const toHistory = () => {
+    router.push(`/pages/customer/trade-history?login=${accountInfo.value.login}`)
+}
+// 按钮对应的操作方法
+const toPosition = () => {
+    router.push(`/pages/customer/trade-position?login=${accountInfo.value.login}`)
+}
+// 按钮对应的操作方法
+const toPaymentHistory = () => {
+    router.push(`/pages/customer/payment-history?login=${accountInfo.value.login}`)
+}
+// 按钮对应的操作方法
+const toDeposit = () => {
+    router.push(`/pages/customer/deposit?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
+}
+const toWithdraw = () => {
+    router.push(`/pages/customer/withdrawal?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
+}
+const toTransfer = () => {
+    router.push(`/pages/customer/transfer?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
+}
+// 格式化余额,拆分为整数和小数部分
+const balanceInteger = computed(() => {
+    return Math.floor(accountInfo.value.balance).toString();
+});
+const balanceDecimal = computed(() => {
+    const parts = accountInfo.value.balance.toFixed(2).split('.');
+    return parts[1] ? '.' + parts[1] : '.00';
+});
+// 在组件挂载后初始化
+onMounted(() => {
+});
+onBeforeUnmount(() => {
+});
+</script>
+
+<style scoped lang="scss">
+@import '@/uni.scss';
+
+.account-card {
+    border-radius: px2rpx(16);
+    padding: px2rpx(16);
+    margin-bottom: px2rpx(16);
+    position: relative;
+    border: 1px solid rgba(108, 133, 149, 0.12);
+    color: #2e3a47;
+
+
+
+
+    .labels-container {
+        display: flex;
+        align-items: center;
+        flex-wrap: wrap;
+        gap: px2rpx(12);
+
+        margin-bottom: px2rpx(16);
+
+        .labels {
+            display: flex;
+            gap: px2rpx(8);
+        }
+
+        .label-badge {
+            background-color: rgba(108, 133, 149, 0.08);
+            border-radius: px2rpx(4);
+            padding: px2rpx(4) px2rpx(8);
+            font-size: px2rpx(12);
+            color: #6c8595;
+            font-weight: 500;
+        }
+
+        .account-number {
+            font-size: px2rpx(18);
+            font-weight: 600;
+            line-height: 1.3;
+        }
+
+        .account-nickname {
+            font-size: px2rpx(14);
+            color: #6c8595;
+        }
+    }
+
+    .main-content {
+        .balance {
+            margin-bottom: px2rpx(16);
+
+            .balance-number {
+                font-size: px2rpx(32);
+                font-weight: 700;
+            }
+
+            .balance-decimal {
+                font-size: px2rpx(16);
+                color: #6c8595;
+                margin-left: px2rpx(4);
+            }
+        }
+
+
+    }
+
+    .info-section {
+        display: flex;
+        flex-direction: column;
+        gap: px2rpx(12);
+        padding: px2rpx(16) 0;
+        margin: px2rpx(16) 0;
+
+        .info-column {
+            flex: 1;
+            display: flex;
+            flex-direction: column;
+            gap: px2rpx(12);
+        }
+
+        .info-item {
+            display: flex;
+            justify-content: space-between;
+            align-items: flex-end;
+            font-size: px2rpx(14);
+
+            .label {
+                color: #6c8595;
+            }
+
+            .line {
+                flex: 1;
+                height: 1px;
+                border-top: 1px dashed rgba(108, 133, 149, 0.5);
+                margin-bottom: px2rpx(1);
+
+            }
+
+            .value {
+                font-weight: 500;
+                color: #2e3a47;
+            }
+        }
+    }
+
+    @media screen and (max-width: 768px) {
+        .info-section {
+            flex-direction: column;
+            gap: px2rpx(12);
+        }
+    }
+
+    .extra-actions {
+        display: flex;
+        align-items: center;
+        gap: px2rpx(8);
+        margin-bottom: px2rpx(12);
+
+        .copy-row {
+            display: flex;
+            align-items: center;
+            gap: px2rpx(4);
+
+            font-size: px2rpx(14);
+
+            .label {
+                color: #6c8595;
+                min-width: px2rpx(30);
+            }
+
+            .value {
+                flex: 1;
+                color: #2e3a47;
+                font-family: monospace;
+            }
+
+            .copy-btn {
+                background: transparent;
+                border: 1px solid rgba(108, 133, 149, 0);
+                padding: 0;
+                cursor: pointer;
+                color: #6c8595;
+                width: px2rpx(24);
+                height: px2rpx(24);
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                box-sizing: border-box;
+
+                svg {
+                    width: px2rpx(20);
+                    height: px2rpx(20);
+                }
+
+                &:hover {
+                    background-color: rgba(108, 133, 149, 0.05);
+                    border: 1px solid rgba(108, 133, 149, 0.2);
+                }
+            }
+        }
+
+        .divider {
+            width: 1px;
+            height: px2rpx(20);
+            border-left: 1px solid rgba(108, 133, 149, 0.5);
+        }
+
+        .change-password-btn {
+            background: transparent;
+            box-sizing: border-box;
+            border: 1px solid rgba(108, 133, 149, 0);
+            border-radius: px2rpx(8);
+            padding: px2rpx(8) px2rpx(16);
+            font-size: px2rpx(14);
+            color: #2e3a47;
+            display: inline-flex;
+            gap: px2rpx(8);
+            cursor: pointer;
+            margin: 0;
+
+            .btn-icon svg {
+                width: px2rpx(16);
+                height: px2rpx(16);
+            }
+
+            &:hover {
+                background-color: rgba(108, 133, 149, 0.05);
+                border: 1px solid rgba(108, 133, 149, 0.2);
+            }
+        }
+    }
+
+
+
+    .notificators {
+        // 预留通知区域
+    }
+}
+
+.card-top {
+    display: flex;
+    justify-content: space-between;
+    align-items: flex-start;
+
+    :deep(.cwg-dropdown-menu-container) {
+        left: px2rpx(-190) !important;
+    }
+
+    .more-btn {
+        flex-shrink: 0;
+        width: px2rpx(40);
+        height: px2rpx(40);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        gap: px2rpx(4);
+        background-color: rgba(108, 133, 149, 0.08);
+        border-radius: px2rpx(4);
+        cursor: pointer;
+
+        &:hover {
+            background-color: rgba(108, 133, 149, 0.1);
+        }
+    }
+
+    // 移动端按钮组
+    .mobile-buttons {
+        display: flex;
+        justify-content: center;
+        gap: px2rpx(12);
+        padding-bottom: px2rpx(16);
+        padding: px2rpx(20);
+
+
+        border-bottom: 1px solid #f0f0f0;
+
+        .circle-btn {
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            cursor: pointer;
+            width: px2rpx(40);
+
+            .circle-icon {
+                width: px2rpx(40);
+                height: px2rpx(40);
+                border-radius: 50%;
+                background-color: #f5f7f9;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                color: #2e3a47;
+                transition: background-color 0.2s;
+
+                &.primary {
+                    background-color: var(--color-navy-700);
+                    color: #fff;
+                }
+
+                &:hover {
+                    background-color: #e9ecef;
+                }
+
+                &.primary:hover {
+                    background-color: var(--color-navy-700);
+                    color: #fff;
+                }
+
+                svg {
+                    width: px2rpx(24);
+                    height: px2rpx(24);
+                }
+            }
+
+            .circle-label {
+                font-size: px2rpx(12);
+                margin-top: px2rpx(4);
+                color: #6c8595;
+            }
+        }
+    }
+
+    .is-disabled {
+        cursor: not-allowed;
+        opacity: 0.5;
+    }
+
+}
+</style>

+ 105 - 7
pages/customer/components/AccountList.vue

@@ -9,10 +9,25 @@
                 </view>
             </view>
         </view>
-        <view class="tabs-class"><cwg-tabs v-model:cativeIndex="cativeIndex" :tabs="tabs" /></view>
-        <view v-if="accounts.length">
+        <view class="tabs-container">
+            <view class="tabs-class">
+                <cwg-tabs v-model:cativeIndex="cativeIndex" :tabs="tabs" />
+            </view>
+            <view class="btn-class">
+                <view class="btn-primary" :class="{ 'btn-primary2': isGridLayout }"
+                    @click="isGridLayout || toggleLayout()">
+                    <cwg-icon icon="crm-card" :size="16" color="#141d2299" />
+                </view>
+                <view class="btn-primary" :class="{ 'btn-primary2': !isGridLayout }"
+                    @click="!isGridLayout || toggleLayout()">
+                    <cwg-icon icon="crm-list" :size="16" color="#141d2299" />
+                </view>
+            </view>
+        </view>
+        <view v-if="accounts.length" :class="{ 'grid-layout': isGridLayout }">
             <AccountCard v-for="acc in accounts" :zhtype="cativeIndex" :key="acc.accountNumber" :account="acc"
-                @action="handleAction" @copy="handleCopy" @change-password="handleChangePassword" />
+                :is-grid-layout="isGridLayout" @action="handleAction" @copy="handleCopy"
+                @change-password="handleChangePassword" />
         </view>
         <cwg-empty-state v-else />
         <DeleteAccountDialogs ref="deleteAccountDialogRef" v-model:visible="deleteAccountDialogVisible" />
@@ -37,7 +52,6 @@ const search = ref({ platform: 'MT4' })
 const isAfterJuly28 = () => {
     const now = new Date();
     const july28 = new Date(2025, 6, 28, 0, 0, 0); // 月份从0开始,所以7月是6
-
     return now >= july28;
 }
 
@@ -55,11 +69,16 @@ const typeMap = computed(() => ({
     8: t('AccountType.CentAccount')
 }));
 const cativeIndex = ref(0)
+const isGridLayout = ref(true)
 const tabs = computed(() => ([
     { id: 'real', name: t('vu.item1') },
     { id: 'demo', name: t('vu.item2') }
 ]))
 
+const toggleLayout = () => {
+    isGridLayout.value = !isGridLayout.value
+}
+
 const tableRef = ref(null)
 const expanded = ref(null)
 const toggleRowExpand = (row) => {
@@ -206,6 +225,7 @@ const accounts = computed(() =>
         labels[1] = acc.platform || 'MT4';
         labels[2] = typeMap.value[acc.type];
         let nickname = typeMap.value[acc.type];
+        let fwq = acc.platform == 'MT4' ? 'MT4 CWGMarketsLtd-Live' : 'MT5 CWGMarketsSVG-Live';
         const balance = acc.balance
         return {
             ...acc,
@@ -214,6 +234,7 @@ const accounts = computed(() =>
             balance,
             accountNumber: acc.login.toString(),
             nickname,
+            fwq,
             balanceWithSymbol: acc.balanceWithSymbol ?? '$0', // 余额
             creditWithSymbol: acc.creditWithSymbol ?? '$0',//信用
             equityWithSymbol: acc.equityWithSymbol ?? '$0',//净值
@@ -241,11 +262,58 @@ onMounted(async () => {
     box-sizing: border-box;
 }
 
-.tabs-class {
-    width: px2rpx(200);
-    margin: px2rpx(20) 0;
+.tabs-container {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    gap: px2rpx(12);
+
+    .tabs-class {
+        width: px2rpx(200);
+        margin: px2rpx(20) 0;
+    }
+
+    .btn-primary {
+        width: px2rpx(26);
+        height: px2rpx(26);
+        border-radius: px2rpx(2);
+        border: none;
+        font-size: px2rpx(14);
+        text-align: center;
+        cursor: pointer;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        gap: px2rpx(8);
+    }
+
+    .btn-primary2 {
+        background-color: rgba(108, 133, 149, 0.08);
+
+    }
+
+    .btn-class {
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        border: 1px solid #f3f4f6;
+        font-size: px2rpx(14);
+        height: px2rpx(34);
+        text-align: center;
+        cursor: pointer;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        gap: px2rpx(8);
+        padding: px2rpx(4) px2rpx(8);
+        box-sizing: border-box;
+    }
+
+    .btn-primary:active {}
 }
 
+
+
 .content-title {
     display: flex;
     justify-content: space-between;
@@ -280,6 +348,36 @@ onMounted(async () => {
         .btn-primary1 {
             background-color: var(--color-navy-700);
         }
+
+        .btn-primary2 {
+            background-color: var(--color-secondary-focus);
+        }
+    }
+}
+
+/* 网格布局样式 */
+.grid-layout {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(25%, 1fr));
+    gap: px2rpx(20);
+
+    @media (max-width: 1200px) {
+        grid-template-columns: repeat(auto-fill, minmax(33.33%, 1fr));
+    }
+
+    @media (max-width: 768px) {
+        grid-template-columns: repeat(auto-fill, minmax(100%, 1fr));
+    }
+
+    .account-card {
+        margin-bottom: 0;
+        height: 100%;
+        display: flex;
+        flex-direction: column;
+    }
+
+    .account-card .main-content {
+        flex: 1;
     }
 }
 

+ 21 - 10
pages/customer/payment-history.vue

@@ -5,7 +5,7 @@
             <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
                 @reset="handleReset" />
             <cwg-tabel ref="tableRef" :columns="columns" :immediate="false" :mobilePrimaryFields="mobilePrimaryFields"
-                :queryParams="search" :api="listApi" :show-operation="false" :showPagination="false">
+                :queryParams="search" :api="listApi" :show-operation="false">
                 <template #avatar="{ row }">
                     <image :src="row.avatar" class="avatar" mode="widthFix" />
                     <cwg-file :path="row.path" />
@@ -116,14 +116,20 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref, nextTick } from 'vue';
+import { computed, ref, nextTick, reactive } from 'vue';
 import { useI18n } from 'vue-i18n';
+import { onLoad } from '@dcloudio/uni-app'
 const { t, locale } = useI18n();
 import { financialApi } from '@/service/financial';
 import { useConfirm } from '@/hooks/useConfirm'
 import { useAccountOptions } from '@/composables/useAccountOptions'
 const { loginOptions } = useAccountOptions()
-const search = ref({})
+const search = reactive({
+    login: null,
+    type: null,
+    orderStatus: null,
+    date: null
+})
 const typeMap = computed(() => ([
     { value: null, text: t('Custom.PaymentHistory.All') },
     { value: 1, text: t('Custom.PaymentHistory.Deposit') },
@@ -236,25 +242,25 @@ const filterFields = computed(() => [
     {
         key: 'orderStatus', type: 'select', label: t('Custom.PaymentHistory.Status'), placeholder: t('placeholder.choose'), options: orderStatusMap.value, defaultValue: null
     },
-    { key: 'login', type: 'select', label: t('Custom.PaymentHistory.TradingAccount'), placeholder: t('placeholder.login'), options: loginOptions || [] },
+    { key: 'login', type: 'select', label: t('Custom.PaymentHistory.TradingAccount'), placeholder: t('placeholder.login'), options: loginOptions || [], defaultValue: search.login || undefined },
     { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
 ])
 
 const searchParams = ref({})
 
 const handleSearch = (params) => {
-    search.value = params
-    search.value.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
-    if (!search.value.platform) return
+    Object.assign(search, params)
+    search.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
+    if (!search.platform) return
     nextTick(() => {
         tableRef.value.refreshTable()
     })
 }
 
 const handleReset = (params) => {
-    search.value = params
-    search.value.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
-    if (!search.value.platform) return
+    Object.assign(search, params)
+    search.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
+    if (!search.platform) return
     nextTick(() => {
         tableRef.value.refreshTable()
     })
@@ -327,6 +333,11 @@ const cancle = async (id, type) => {
         console.log('取消删除')
     }
 }
+onLoad((e) => {
+    if (e.login) {
+        search.login = Number(e.login)
+    }
+})
 </script>
 
 <style scoped lang="scss">

+ 1 - 1
pages/customer/recording-history.vue

@@ -5,7 +5,7 @@
             <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" :showPagination="false">
+                :show-operation="false">
                 <!-- 状态列自定义渲染 -->
                 <template #status="{ row }">
                     <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">

+ 27 - 27
pages/customer/trade-history.vue

@@ -4,16 +4,15 @@
         <view class="info-card">
             <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
                 @reset="handleReset" />
-
             <cwg-tabel ref="tableRef" :columns="columns" :immediate="false" :mobilePrimaryFields="mobilePrimaryFields"
-                :queryParams="search" :api="listApi" :show-operation="false" :showPagination="false">
+                :queryParams="search" :api="listApi" :show-operation="false">
                 <template #symbol="{ row }">
                     <view class="symbol-cell">
                         <view class="pair">{{ getSymbolParts(row.symbol)[0] }}/{{ getSymbolParts(row.symbol)[1]
-                        }}</view>
+                            }}</view>
                         <view class="desc">{{ row.openPrice }}
                             <text :class="getCmdColorClass(row.cmdName)">{{ formatCmdName(row.cmdName) }}{{ row.volume
-                            }}{{ t('Label.Lot') }}</text>
+                                }}{{ t('Label.Lot') }}</text>
                         </view>
                     </view>
                 </template>
@@ -28,27 +27,19 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref, nextTick } from 'vue';
+import { computed, ref, nextTick, reactive } from 'vue';
 import { useI18n } from 'vue-i18n';
+import { onLoad } from '@dcloudio/uni-app'
 const { t, locale } = useI18n();
 import { customApi } from '@/service/custom';
 import { useAccountOptions } from '@/composables/useAccountOptions'
 const { loginOptions } = useAccountOptions()
-const search = ref()
-const typeMap = computed(() => ([
-    { value: null, text: t('Custom.PaymentHistory.All') },
-    { value: 1, text: t('Custom.PaymentHistory.Deposit') },
-    { value: 2, text: t('Custom.PaymentHistory.Withdrawals') }
-]));
-const orderStatusMap = computed(() => ([
-    { value: null, text: t('Custom.PaymentHistory.All') },
-    { value: 1, text: t('State.ToBeProcessed') },
-    { value: 2, text: t('State.Completed') },
-    { value: 3, text: t('State.InTheProcessing') },
-    { value: 4, text: t('State.Refused') },
-    { value: 5, text: t('State.expireTime') },
-    { value: 6, text: t('State.Cancelled') },
-]));
+const search = reactive({
+    login: null,
+    type: null,
+    orderStatus: null,
+    date: null
+})
 
 // 表格列配置
 const columns = computed(() => [
@@ -136,24 +127,25 @@ const mobilePrimaryFields = computed(() => [
 
 // 动态传入筛选字段配置
 const filterFields = computed(() => [
-    { key: 'login', type: 'select', label: t('Custom.PaymentHistory.TradingAccount'), placeholder: t('placeholder.login'), options: loginOptions || [] },
+    { key: 'login', type: 'select', label: t('Custom.PaymentHistory.TradingAccount'), placeholder: t('placeholder.login'), options: loginOptions || [], defaultValue: search?.login || undefined },
     { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
 ])
 const searchParams = ref({})
 const tableRef = ref(null)
 const handleSearch = (params) => {
-    search.value = params
-    search.value.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
-    if (!search.value.platform) return
+    Object.assign(search, params)
+    search.login = Number(params.login)
+    search.platform = loginOptions.find(item => item.value == params.login)?.platform || ''
+    if (!search.platform) return
     nextTick(() => {
         tableRef.value.refreshTable()
     })
 }
 
 const handleReset = (params) => {
-    search.value = params
-    search.value.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
-    if (!search.value.platform) return
+    Object.assign(search, params)
+    search.platform = loginOptions.find(item => item.value == params.login)?.platform || ''
+    if (!search.platform) return
     nextTick(() => {
         tableRef.value.refreshTable()
     })
@@ -189,6 +181,14 @@ const getProfitColorClass = (profit: any) => {
     if (!Number.isFinite(n) || n === 0) return ''
     return n > 0 ? 'is-profit' : 'is-loss'
 }
+
+onLoad((e) => {
+    console.log(e, 'e')
+    if (e.login) {
+        // ✅ 必须转数字!你的 value 是数字类型
+        search.login = Number(e.login)
+    }
+})
 </script>
 
 <style scoped lang="scss">

+ 20 - 10
pages/customer/trade-position.vue

@@ -5,7 +5,7 @@
             <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
                 @reset="handleReset" />
             <cwg-tabel ref="tableRef" :columns="columns" :immediate="false" :mobilePrimaryFields="mobilePrimaryFields"
-                :queryParams="search" :api="listApi" :show-operation="false" :showPagination="false">
+                :queryParams="search" :api="listApi" :show-operation="false">
                 <template #symbol="{ row }">
                     <view class="symbol-cell">
                         <view class="pair">{{ getSymbolParts(row.symbol)[0] }}/{{ getSymbolParts(row.symbol)[1]
@@ -27,13 +27,16 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref, nextTick } from 'vue';
+import { computed, ref, nextTick, reactive } from 'vue';
 import { useI18n } from 'vue-i18n';
 const { t, locale } = useI18n();
+import { onLoad } from '@dcloudio/uni-app'
 import { customApi } from '@/service/custom';
 import { useAccountOptions } from '@/composables/useAccountOptions'
 const { loginOptions } = useAccountOptions()
-const search = ref()
+const search = reactive({
+    login: null
+})
 const typeMap = computed(() => ([
     { value: null, text: t('Custom.PaymentHistory.All') },
     { value: 1, text: t('Custom.PaymentHistory.Deposit') },
@@ -130,24 +133,25 @@ const mobilePrimaryFields = computed(() => [
 
 // 动态传入筛选字段配置
 const filterFields = computed(() => [
-    { key: 'login', type: 'select', label: t('Custom.PaymentHistory.TradingAccount'), placeholder: t('placeholder.login'), options: loginOptions || [] },
+    { key: 'login', type: 'select', label: t('Custom.PaymentHistory.TradingAccount'), placeholder: t('placeholder.login'), options: loginOptions || [], defaultValue: search.login || undefined },
     { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
 ])
 const searchParams = ref({})
 const tableRef = ref(null)
 const handleSearch = (params) => {
-    search.value = params
-    search.value.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
-    if(!search.value.platform) return
+    Object.assign(search, params)
+    search.login = Number(params.login)
+    search.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
+    if(!search.platform) return
     nextTick(() => {
         tableRef.value.refreshTable()
     })
 }
 
 const handleReset = (params) => {
-    search.value = params
-    search.value.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
-    if(!search.value.platform) return
+    Object.assign(search, params)
+    search.platform = loginOptions.find(item => item.value === params.login)?.platform || ''
+    if(!search.platform) return
     nextTick(() => {
         tableRef.value.refreshTable()
     })
@@ -184,6 +188,12 @@ const getProfitColorClass = (profit: any) => {
     if (!Number.isFinite(n) || n === 0) return ''
     return n > 0 ? 'is-profit' : 'is-loss'
 }
+
+onLoad((e) => {
+    if (e.login) {
+        search.login = Number(e.login)
+    }
+})
 </script>
 
 <style scoped lang="scss">

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

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-layout-grid"><path d="M4 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"></path><path d="M14 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"></path><path d="M4 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"></path><path d="M14 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"></path></svg>

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

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tabler-icon tabler-icon-list"><path d="M9 6l11 0"></path><path d="M9 12l11 0"></path><path d="M9 18l11 0"></path><path d="M5 6l0 .01"></path><path d="M5 12l0 .01"></path><path d="M5 18l0 .01"></path></svg>