Explorar o código

feat:跟单

ljc hai 1 mes
pai
achega
c4338505ac

+ 44 - 0
components/cwg-payment.vue

@@ -9,6 +9,10 @@
         <cwg-icon name="crm-payment" color="#141d22" />
         <text class="balance-text">{{ ibBalance }} USD</text>
       </view>
+      <view class="pc-header-btn pc-payment-btn" v-if="mode === 'follow'">
+        <cwg-icon name="crm-payment" color="#141d22" />
+        <text class="balance-text">{{ followBalance }} USD</text>
+      </view>
       <template #btn>
         <view class="right-drawer custom-payment-drawer" v-if="mode === 'customer'">
           <view class="drawer-header">
@@ -47,6 +51,19 @@
             <button class="action-btn" @click.stop="goIbPages(3)" v-t="'Ib.Transfer.CommissionIssue'"></button>
           </view>
         </view>
+        <view class="right-drawer custom-payment-drawer" v-if="mode === 'follow'">
+          <view class="drawer-header">
+            <text class="drawer-title">隐藏余额</text>
+            <switch :checked="!isShow" @change="toggleShow" color="#6c8595" style="transform:scale(0.7)" />
+          </view>
+          <view class="drawer-content">
+            <view class="balance-amount">{{t('Documentary.console.item15')}}{{ followBalance }} USD</view>
+
+          </view>
+          <view class="drawer-actions">
+            <button class="action-btn" @click.stop="goFollowPages(1)" v-t="'Documentary.console.item17'"></button>
+          </view>
+        </view>
       </template>
     </cwg-dropdown>
   </view>
@@ -64,6 +81,7 @@
   import { userToken } from '@/composables/config'
   import { onLoad } from '@dcloudio/uni-app'
   import { ibApi } from '@/service/ib'
+  import { documentaryApi } from '@/service/documentary'
 
   const { mode } = useMenuSplit()
 
@@ -78,6 +96,7 @@
   const router = useRouter()
   const menuList = []
   const ibData = ref({})
+  const walletData = ref({})
 
   const handleMenuClick = ({ value }) => {
     goPages(value)
@@ -125,6 +144,11 @@
     const value = NumberDecimal(ibData.value?.all || 0)
     return isShow.value ? value : NumberDesensitization(value)
   })
+  // 跟单
+  const followBalance = computed(() => {
+    const value = NumberDecimal(walletData.value?.walletAmount || 0)
+    return isShow.value ? value : NumberDesensitization(value)
+  })
   const getWalletList = async () => {
     let res = await drawApi.walletbalance({})
     if (res.code == 200) {
@@ -157,6 +181,20 @@
   const toggleShow = (e) => {
     isShow.value = !e.detail.value
   }
+  //跟单金额
+  const getMoneyList = async () => {
+    let res = await documentaryApi.followWalletSingle({})
+    if (res.code == 200) {
+      if (res.data != null) {
+        walletData.value = res.data
+      }
+    } else {
+      uni.showToast({
+        title: res.msg,
+        icon: 'none',
+      })
+    }
+  }
 
   const goPages = (type) => {
     let path
@@ -169,6 +207,12 @@
     close()
   }
 
+  const goFollowPages = (type) => {
+    // let path
+    router.push('/pages/follow/transfer')
+    close()
+  }
+
   const goIbPages = (type) => {
     let path,query
     if (type == 1) {

+ 318 - 296
components/cwg-sidebar.vue

@@ -1,235 +1,257 @@
 <template>
-    <view class="cwg-sidebar">
-        <view class="wallet-widget">
-            <view class="wallet-header" :class="{ 'header-bottom': isWalletOpen }"
-                @click="isWalletOpen = !isWalletOpen">
-                <view class="wallet-header-left">
-                    <cwg-icon name="crm-payment" :size="16" color="#141d22" />
-                    <text class="wallet-header-text">{{ mode === 'customer' ? formattedBalance : ibBalance }} USD</text>
-                </view>
-                <view class="wallet-header-right" :class="{ 'expanded': isWalletOpen }">
-                    <cwg-icon name="crm-chevron-down" :size="16" color="#6c8595" />
-                </view>
-            </view>
+  <view class="cwg-sidebar">
+    <view class="wallet-widget">
+      <view class="wallet-header" :class="{ 'header-bottom': isWalletOpen }"
+            @click="isWalletOpen = !isWalletOpen">
+        <view v-if="mode=='follow'" class="wallet-header-left">
+          <cwg-icon name="crm-payment" :size="16" color="#141d22" />
+          <text>{{ t('Documentary.console.item15') }}</text>
+          <text class="wallet-header-text">{{ followBalance }} USD</text>
+        </view>
+        <view v-else class="wallet-header-left">
+          <cwg-icon name="crm-payment" :size="16" color="#141d22" />
+          <text class="wallet-header-text">{{ mode === 'customer' ? formattedBalance : ibBalance }} USD</text>
+        </view>
+        <view class="wallet-header-right" :class="{ 'expanded': isWalletOpen }">
+          <cwg-icon name="crm-chevron-down" :size="16" color="#6c8595" />
+        </view>
+      </view>
 
-            <view class="wallet-body" v-if="isWalletOpen">
-                <view class="wallet-body-header">
-                    <text class="drawer-title">隐藏余额</text>
-                    <switch :checked="!isShow" @change="toggleShow" color="#6c8595"
-                        style="transform:scale(0.7); margin-right: -10px;" />
-                </view>
-
-                <view class="wallet-body-content">
-                    <view class="balance-amount">{{ mode === 'customer' ? formattedBalance : ibBalance }} USD</view>
-                    <view class="wallet-type">{{ mode === 'customer' ? t('wallet.pendingWithdraw') :
-                        t('Ib.Index.TotalRevenue') }}</view>
-                    <view class="wallet-id-box">
-                        {{ mode === 'customer' ? formattedPendingWithdrawAmount : ibTotalBalance }}
-                    </view>
-                </view>
-
-                <view class="wallet-actions" v-if="mode === 'customer'">
-                    <button class="action-btn" @click.stop="goPages(1)" v-t="'wallet.item6'"></button>
-                    <button class="action-btn" @click.stop="goPages(2)" v-t="'wallet.item7'"></button>
-                </view>
-                <view class="wallet-actions" v-if="mode === 'ib'">
-                    <button class="action-btn" @click.stop="goIbPages(1)" v-t="'Custom.Index.Withdrawals'"></button>
-                    <button class="action-btn" @click.stop="goIbPages(2)" v-t="'Home.page_ib.item4'"></button>
-                    <!--                  <button class="action-btn" @click.stop="goIbPages(3)" v-t="'Ib.Transfer.CommissionIssue'"></button>-->
-                </view>
+      <view class="wallet-body" v-if="isWalletOpen">
+        <view class="wallet-body-header">
+          <text class="drawer-title">隐藏余额</text>
+          <switch :checked="!isShow" @change="toggleShow" color="#6c8595"
+                  style="transform:scale(0.7); margin-right: -10px;" />
+        </view>
+
+        <view class="wallet-body-content">
+          <template v-if="mode==='follow'">
+            <view class="balance-amount">{{ followBalance }} USD</view>
+          </template>
+          <template v-else>
+            <view class="balance-amount">{{ mode === 'customer' ? formattedBalance : ibBalance }} USD</view>
+            <view class="wallet-type">{{ mode === 'customer' ? t('wallet.pendingWithdraw') :
+              t('Ib.Index.TotalRevenue') }}
+            </view>
+            <view class="wallet-id-box">
+              {{ mode === 'customer' ? formattedPendingWithdrawAmount : ibTotalBalance }}
             </view>
+          </template>
+
+        </view>
+
+        <view class="wallet-actions" v-if="mode === 'customer'">
+          <button class="action-btn" @click.stop="goPages(1)" v-t="'wallet.item6'"></button>
+          <button class="action-btn" @click.stop="goPages(2)" v-t="'wallet.item7'"></button>
+        </view>
+        <view class="wallet-actions" v-if="mode === 'ib'">
+          <button class="action-btn" @click.stop="goIbPages(1)" v-t="'Custom.Index.Withdrawals'"></button>
+          <button class="action-btn" @click.stop="goIbPages(2)" v-t="'Home.page_ib.item4'"></button>
+          <!--                  <button class="action-btn" @click.stop="goIbPages(3)" v-t="'Ib.Transfer.CommissionIssue'"></button>-->
+        </view>
+        <view class="wallet-actions" v-if="mode === 'follow'">
+          <button class="action-btn" @click.stop="goFollow()" v-t="'Documentary.console.item17'"></button>
         </view>
+      </view>
+    </view>
 
-        <view class="menu-list">
-            <view class="menu" v-for="(item, index) in menus" :key="item.path">
-                <view class="menu-item" @click="handleClick(index)">
-                    <cwg-icon :name="item.icon" :size="20" color="#6c8595" />
-                    <view class="menu-label" v-t="item.label" />
-                    <view class="chevron-icon" :class="{ 'expanded': item.isOpenMenu }">
-                        <cwg-icon v-if="item.children && item.children.length" name="crm-chevron-down" :size="20"
-                            color="#6c8595" />
-                    </view>
-                </view>
-                <view :ref="(el) => setSubmenuRef(index, el)" class="submenu-box" :a="index" :key1="item.path + index"
-                    :b="item" :style="{
+    <view class="menu-list">
+      <view class="menu" v-for="(item, index) in menus" :key="item.path">
+        <view class="menu-item" @click="handleClick(index)">
+          <cwg-icon :name="item.icon" :size="20" color="#6c8595" />
+          <view class="menu-label" v-t="item.label" />
+          <view class="chevron-icon" :class="{ 'expanded': item.isOpenMenu }">
+            <cwg-icon v-if="item.children && item.children.length" name="crm-chevron-down" :size="20"
+                      color="#6c8595" />
+          </view>
+        </view>
+        <view :ref="(el) => setSubmenuRef(index, el)" class="submenu-box" :a="index" :key1="item.path + index"
+              :b="item" :style="{
                         height: !(item.children && item.children.length) ? '0px' : item.isOpenMenu ? item.submenuHeight + 'px' : '0px',
                         transition: 'height 281ms cubic-bezier(0.4, 0, 0.2, 1)'
                     }" :class="{ 'active': item.isOpenMenu }">
-                    <cwg-submenu v-if="item.children && item.children.length" :submenu-items="item.children"
-                        @submenu-click="handleClick1" />
-                </view>
-            </view>
-        </view>
-        <view class="menu fixed">
-            <view class="menu-item ib-box" @click="setMode('customer')" v-if="mode !== 'customer'">
-                <cwg-icon name="crm-trade" :size="20" color="#6c8595" />
-                <view class="menu-label" v-t="'Home.msg.Custom'" />
-            </view>
-            <view class="menu-item ib-box" @click="setMode('ib')" v-if="mode !== 'ib' && ibStatus">
-                <cwg-icon name="crm-ib" :size="20" color="#6c8595" />
-                <view class="menu-label" v-t="'Home.msg.Ib'" />
-            </view>
-            <view class="menu-item ib-box" @click="setMode('follow')" v-if="mode !== 'follow'">
-                <cwg-icon name="crm-gd" :size="20" color="#6c8595" />
-                <view class="menu-label" v-t="'Documentary.title'" />
-            </view>
+          <cwg-submenu v-if="item.children && item.children.length" :submenu-items="item.children"
+                       @submenu-click="handleClick1" />
         </view>
+      </view>
+    </view>
+    <view class="menu fixed">
+      <view class="menu-item ib-box" @click="setMode('customer')" v-if="mode !== 'customer'">
+        <cwg-icon name="crm-trade" :size="20" color="#6c8595" />
+        <view class="menu-label" v-t="'Home.msg.Custom'" />
+      </view>
+      <view class="menu-item ib-box" @click="setMode('ib')" v-if="mode !== 'ib' && ibStatus">
+        <cwg-icon name="crm-ib" :size="20" color="#6c8595" />
+        <view class="menu-label" v-t="'Home.msg.Ib'" />
+      </view>
+      <view class="menu-item ib-box" @click="setMode('follow')" v-if="mode !== 'follow'">
+        <cwg-icon name="crm-gd" :size="20" color="#6c8595" />
+        <view class="menu-label" v-t="'Documentary.title'" />
+      </view>
     </view>
+  </view>
 </template>
 
 <script lang="ts" setup>
-import { ref, computed, watch, onMounted } from 'vue'
-import useUserStore from '@/stores/use-user-store'
-import { useMenuSplit } from '@/composables/useMenuSplit'
-import { storeToRefs } from 'pinia'
-import { drawApi } from '@/service/draw'
-import { ibApi } from '@/service/ib'
-import { userToken } from '@/composables/config'
-import useRouter from '@/hooks/useRouter'
-import { useI18n } from 'vue-i18n'
-
-const { t } = useI18n()
-const router = useRouter()
-const handleClick1 = (item: MenuItem) => {
+  import { ref, computed, watch, onMounted } from 'vue'
+  import useUserStore from '@/stores/use-user-store'
+  import { useMenuSplit } from '@/composables/useMenuSplit'
+  import { storeToRefs } from 'pinia'
+  import { drawApi } from '@/service/draw'
+  import { ibApi } from '@/service/ib'
+  import { userToken } from '@/composables/config'
+  import useRouter from '@/hooks/useRouter'
+  import { useI18n } from 'vue-i18n'
+
+  const { t } = useI18n()
+  const router = useRouter()
+  const handleClick1 = (item: MenuItem) => {
     emit('handle-click', item)
-}
-const { menus, setSubmenuRef, setMode, handleClick, handleSubmenuClick, mode } = useMenuSplit(handleClick1)
-const userStore = useUserStore()
-const { userInfo } = storeToRefs(userStore)
+  }
+  const { menus, setSubmenuRef, setMode, handleClick, handleSubmenuClick, mode } = useMenuSplit(handleClick1)
+  const userStore = useUserStore()
+  const { userInfo } = storeToRefs(userStore)
 
-const emit = defineEmits(['handle-click'])
+  const emit = defineEmits(['handle-click'])
 
-// ib按钮展示
-const ibStatus = computed(() => {
+  // ib按钮展示
+  const ibStatus = computed(() => {
     const info: any = userInfo.value
     return !!info && !!info.customInfo && info.customInfo.ibInvalid == 0 && !!info.ibInfo
-})
+  })
 
-const isWalletOpen = ref(true)
-const isShow = ref(true)
-const walletbalance = ref(0)
-const pendingWithdrawAmount = ref(0)
-const ibData = ref({} as any)
+  const isWalletOpen = ref(true)
+  const isShow = ref(true)
+  const walletbalance = ref(0)
+  const pendingWithdrawAmount = ref(0)
+  const ibData = ref({} as any)
 
-const NumberDecimal = (value: any) => {
+  const NumberDecimal = (value: any) => {
     let realVal = ''
     if (!isNaN(value) && value !== '') {
-        realVal = parseFloat(value).toFixed(2)
+      realVal = parseFloat(value).toFixed(2)
     } else {
-        realVal = '0.00'
+      realVal = '0.00'
     }
     return realVal
-}
-const NumberDesensitization = (value: any) => {
+  }
+  const NumberDesensitization = (value: any) => {
     let realVal = ''
     if (!isNaN(value) && value !== '') {
-        value = value.toString()
-        realVal = value.substr(0, 2) + '****' + value.substr(-2)
+      value = value.toString()
+      realVal = value.substr(0, 2) + '****' + value.substr(-2)
     } else {
-        realVal = '--'
+      realVal = '--'
     }
     return realVal
-}
+  }
 
-const formattedBalance = computed(() => {
+  const formattedBalance = computed(() => {
     const value = walletbalance.value || '0'
     const decimalValue = NumberDecimal(value)
     return isShow.value ? decimalValue : NumberDesensitization(decimalValue)
-})
+  })
 
-const ibBalance = computed(() => {
+  const ibBalance = computed(() => {
     const value = NumberDecimal(ibData.value?.balance || 0)
     return isShow.value ? value : NumberDesensitization(value)
-})
+  })
 
-const formattedPendingWithdrawAmount = computed(() => {
+  const formattedPendingWithdrawAmount = computed(() => {
     const value = pendingWithdrawAmount.value || '0'
     const decimalValue = NumberDecimal(value)
     return isShow.value ? decimalValue : NumberDesensitization(decimalValue)
-})
+  })
 
-const ibTotalBalance = computed(() => {
+  const ibTotalBalance = computed(() => {
     const value = NumberDecimal(ibData.value?.all || 0)
     return isShow.value ? value : NumberDesensitization(value)
-})
-const getWalletList = async () => {
+  })
+  const followBalance = computed(() => {
+    const value = NumberDecimal(ibData.value?.all || 0)
+    return isShow.value ? value : NumberDesensitization(value)
+  })
+  const getWalletList = async () => {
     let res = await drawApi.walletbalance({})
     if (res.code == 200) {
-        if (res.data != null) {
-            walletbalance.value = res.data
-        }
+      if (res.data != null) {
+        walletbalance.value = res.data
+      }
     } else {
-        uni.showToast({ title: res.msg, icon: 'none' })
+      uni.showToast({ title: res.msg, icon: 'none' })
     }
-}
+  }
 
-//获取处理中出金金额
-const getPendingWithdrawAmount = async () => {
+  //获取处理中出金金额
+  const getPendingWithdrawAmount = async () => {
     let res = await drawApi.pendingWithdrawAmount({})
     if (res.code == 200) {
-        if (res.data != null) {
-            pendingWithdrawAmount.value = res.data
-        }
+      if (res.data != null) {
+        pendingWithdrawAmount.value = res.data
+      }
     } else {
-        uni.showToast({
-            title: res.msg,
-            icon: 'none',
-        })
+      uni.showToast({
+        title: res.msg,
+        icon: 'none',
+      })
     }
-}
+  }
 
-const getIbData = async () => {
+  const getIbData = async () => {
     const res = await ibApi.IbData()
     if (res.code === 200) {
-        if (res.data != null) ibData.value = res.data
+      if (res.data != null) ibData.value = res.data
     } else {
-        uni.showToast({ title: res.msg, icon: 'none' })
+      uni.showToast({ title: res.msg, icon: 'none' })
     }
-}
+  }
 
-watch(() => mode.value, (newMode) => {
+  watch(() => mode.value, (newMode) => {
     if (!userToken.value) return
     if (newMode == 'customer') {
-        getWalletList()
-        getPendingWithdrawAmount()
+      getWalletList()
+      getPendingWithdrawAmount()
     } else if (newMode == 'ib') {
-        getIbData()
+      getIbData()
     }
-}, { immediate: true })
+  }, { immediate: true })
 
-const toggleShow = (e: any) => {
+  const toggleShow = (e: any) => {
     isShow.value = !e.detail.value
-}
+  }
 
-const goPages = (type: number) => {
+  const goPages = (type: number) => {
     let path = ''
     if (type == 1) {
-        path = '/pages/customer/wallet-transfer'
+      path = '/pages/customer/wallet-transfer'
     } else if (type == 2) {
-        path = '/pages/customer/wallet-history'
+      path = '/pages/customer/wallet-history'
     }
     if (path) router.push(path)
-}
+  }
+  const goFollow = (type: number) => {
+    router.push('/pages/follow/transfer')
+  }
 
-const goIbPages = (type: number) => {
+  const goIbPages = (type: number) => {
     let path = ''
     let query = {}
     if (type == 1) {
-        path = '/pages/ib/withdraw-select'
+      path = '/pages/ib/withdraw-select'
     } else if (type == 2) {
-        path = '/pages/ib/transfer'
+      path = '/pages/ib/transfer'
     } else if (type == 3) {
-        path = '/pages/ib/transfer'
-        query = { tab: 2 }
+      path = '/pages/ib/transfer'
+      query = { tab: 2 }
     }
     if (path) router.push({ path, query })
-}
+  }
 </script>
 
 <style scoped lang="scss">
-@import "@/uni.scss";
+  @import "@/uni.scss";
 
-.cwg-sidebar {
+  .cwg-sidebar {
     width: px2rpx(280);
     color: #6c8595;
     height: calc(100vh - 56px);
@@ -244,199 +266,199 @@ const goIbPages = (type: number) => {
     border-right: 1px solid rgba(108, 133, 149, 0.12);
 
     .wallet-widget {
-        width: 100%;
-        //border-radius: px2rpx(4);
-        border-top: 1px solid rgba(108, 133, 149, 0.12);
+      width: 100%;
+      //border-radius: px2rpx(4);
+      border-top: 1px solid rgba(108, 133, 149, 0.12);
+      border-bottom: 1px solid rgba(108, 133, 149, 0.12);
+      overflow: hidden;
+      margin-bottom: px2rpx(4);
+
+      .header-bottom {
         border-bottom: 1px solid rgba(108, 133, 149, 0.12);
-        overflow: hidden;
-        margin-bottom: px2rpx(4);
+      }
 
-        .header-bottom {
-            border-bottom: 1px solid rgba(108, 133, 149, 0.12);
+      .wallet-header {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: px2rpx(10) px2rpx(12);
+        //background: #f4f6f8;
+
+        cursor: pointer;
+
+        .wallet-header-left {
+          display: flex;
+          align-items: center;
+          gap: px2rpx(8);
         }
 
-        .wallet-header {
-            display: flex;
-            align-items: center;
-            justify-content: space-between;
-            padding: px2rpx(10) px2rpx(12);
-            //background: #f4f6f8;
+        .wallet-header-text {
+          font-size: 14px;
+          color: #141d22;
+          font-weight: 500;
+        }
 
-            cursor: pointer;
+        .wallet-header-right {
+          transition: transform 0.3s;
 
-            .wallet-header-left {
-                display: flex;
-                align-items: center;
-                gap: px2rpx(8);
-            }
+          &.expanded {
+            transform: rotate(180deg);
+          }
+        }
+      }
+
+      .wallet-body {
+        padding: px2rpx(12);
+        background: #ffffff;
+
+        .wallet-body-header {
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          margin-bottom: px2rpx(12);
+
+          .drawer-title {
+            font-size: 13px;
+            color: #6c8595;
+          }
+        }
 
-            .wallet-header-text {
-                font-size: 14px;
-                color: #141d22;
-                font-weight: 500;
-            }
+        .wallet-body-content {
+          margin-bottom: px2rpx(16);
 
-            .wallet-header-right {
-                transition: transform 0.3s;
+          .balance-amount {
+            font-size: 16px;
+            font-weight: 600;
+            color: #141d22;
+            margin-bottom: px2rpx(4);
+          }
 
-                &.expanded {
-                    transform: rotate(180deg);
-                }
+          .wallet-type {
+            font-size: 12px;
+            color: #999;
+            margin-bottom: px2rpx(4);
+          }
+
+          .wallet-id-box {
+            display: flex;
+            align-items: center;
+            gap: px2rpx(4);
+
+            .wallet-id {
+              font-size: 12px;
+              color: #999;
             }
+          }
         }
 
-        .wallet-body {
-            padding: px2rpx(12);
-            background: #ffffff;
+        .wallet-actions {
+          display: flex;
+          gap: px2rpx(8);
 
-            .wallet-body-header {
-                display: flex;
-                align-items: center;
-                justify-content: space-between;
-                margin-bottom: px2rpx(12);
-
-                .drawer-title {
-                    font-size: 13px;
-                    color: #6c8595;
-                }
-            }
+          .action-btn {
+            flex: 1;
+            height: px2rpx(32);
+            line-height: px2rpx(32);
+            background-color: #f5f7fa;
+            color: #141d22;
+            font-size: 13px;
+            border-radius: px2rpx(4);
+            margin: 0;
 
-            .wallet-body-content {
-                margin-bottom: px2rpx(16);
-
-                .balance-amount {
-                    font-size: 16px;
-                    font-weight: 600;
-                    color: #141d22;
-                    margin-bottom: px2rpx(4);
-                }
-
-                .wallet-type {
-                    font-size: 12px;
-                    color: #999;
-                    margin-bottom: px2rpx(4);
-                }
-
-                .wallet-id-box {
-                    display: flex;
-                    align-items: center;
-                    gap: px2rpx(4);
-
-                    .wallet-id {
-                        font-size: 12px;
-                        color: #999;
-                    }
-                }
+            &::after {
+              border: none;
             }
 
-            .wallet-actions {
-                display: flex;
-                gap: px2rpx(8);
-
-                .action-btn {
-                    flex: 1;
-                    height: px2rpx(32);
-                    line-height: px2rpx(32);
-                    background-color: #f5f7fa;
-                    color: #141d22;
-                    font-size: 13px;
-                    border-radius: px2rpx(4);
-                    margin: 0;
-
-                    &::after {
-                        border: none;
-                    }
-
-                    &:active {
-                        background-color: #e4e7ed;
-                    }
-                }
+            &:active {
+              background-color: #e4e7ed;
             }
+          }
         }
+      }
     }
 
     .logo {
-        width: px2rpx(54);
+      width: px2rpx(54);
     }
 
     .menu-list {
-        flex: 1;
-        width: 100%;
-        overflow-y: auto;
-        display: flex;
-        flex-direction: column;
-        gap: px2rpx(8);
+      flex: 1;
+      width: 100%;
+      overflow-y: auto;
+      display: flex;
+      flex-direction: column;
+      gap: px2rpx(8);
     }
 
     .submenu-box {
-        width: 100%;
-        height: 0;
-        overflow: hidden;
+      width: 100%;
+      height: 0;
+      overflow: hidden;
     }
 
     .menu {
-        width: 100%;
-        position: relative;
-        display: flex;
-        flex-direction: column;
-        align-items: center;
-        box-sizing: border-box;
+      width: 100%;
+      position: relative;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      box-sizing: border-box;
 
     }
 
     .menu-item {
-        width: 100%;
-        height: px2rpx(40);
-        cursor: pointer;
-        display: flex;
-        align-items: center;
-        justify-content: space-between;
-        gap: px2rpx(8);
-        padding: px2rpx(10);
-        box-sizing: border-box;
-        font-size: 14px;
-
-        .menu-label {
-            flex: 1;
-        }
+      width: 100%;
+      height: px2rpx(40);
+      cursor: pointer;
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      gap: px2rpx(8);
+      padding: px2rpx(10);
+      box-sizing: border-box;
+      font-size: 14px;
+
+      .menu-label {
+        flex: 1;
+      }
 
-        &:hover {
-            background: rgba(108, 133, 149, 0.12) !important;
-            border: 1px solid rgb(145, 163, 176) !important;
-            border-radius: px2rpx(4);
-        }
+      &:hover {
+        background: rgba(108, 133, 149, 0.12) !important;
+        border: 1px solid rgb(145, 163, 176) !important;
+        border-radius: px2rpx(4);
+      }
 
-        .expanded .icon {
-            transform: rotate(180deg);
-        }
+      .expanded .icon {
+        transform: rotate(180deg);
+      }
     }
 
     .ib-box {
+      background: rgba(140, 69, 246, 0.08) !important;
+      border: 1px solid rgba(140, 69, 246, 0.2) !important;
+      font-size: px2rpx(18);
+      font-weight: 600;
+      color: #141d22;
+
+      &:hover {
         background: rgba(140, 69, 246, 0.08) !important;
         border: 1px solid rgba(140, 69, 246, 0.2) !important;
-        font-size: px2rpx(18);
-        font-weight: 600;
-        color: #141d22;
-
-        &:hover {
-            background: rgba(140, 69, 246, 0.08) !important;
-            border: 1px solid rgba(140, 69, 246, 0.2) !important;
-        }
+      }
     }
 
     .zy-box {
-        display: flex;
-        align-items: center;
-        justify-content: center;
+      display: flex;
+      align-items: center;
+      justify-content: center;
     }
 
     .fixed {
-        position: relative;
-        width: 100%;
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        gap: px2rpx(8);
+      position: relative;
+      width: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      gap: px2rpx(8);
     }
-}
+  }
 </style>

+ 365 - 0
pages/follow/components/applySignalDialog.vue

@@ -0,0 +1,365 @@
+<template>
+  <cwg-popup :title="t('Documentary.TundManagement.item41')" :visible="visible" @close="closeDia" @confirm="confirmDia">
+    <view class="dialog-content">
+      <uni-forms ref="formRef" :model="formData" :rules="rules" labelWidth="200" label-position="top" class="crm-form">
+        <!-- 名片信息 -->
+        <view class="fllow-title">
+          <text>{{ t('Documentary.tradingCenter.item126') }}</text>
+        </view>
+        <uni-forms-item :label="t('Documentary.console.item20')" name="nickname">
+          <uni-easyinput v-model="formData.nickname" :placeholder="t('placeholder.input')" />
+        </uni-forms-item>
+        
+        <uni-row :gutter="20" class="responsive-row">
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Documentary.console.item21')" name="personalSignature">
+              <uni-easyinput type="textarea" v-model="formData.personalSignature" :maxlength="100" @input="val => filterChineseEnglishOnly('personalSignature', val)" :placeholder="t('placeholder.input')" />
+            </uni-forms-item>
+          </uni-col>
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Documentary.console.item22')" name="traderStrategy">
+              <uni-easyinput type="textarea" v-model="formData.traderStrategy" :maxlength="150" @input="val => filterChineseEnglishOnly('traderStrategy', val)" :placeholder="t('placeholder.input')" />
+            </uni-forms-item>
+          </uni-col>
+        </uni-row>
+        
+        <view style="color: red; font-size: 12px; margin-bottom: 10px;">
+          <text>{{ t('Documentary.console.item37') }}</text>
+        </view>
+
+        <!-- 账户信息 -->
+        <view class="fllow-title">
+          <text>{{ t('Documentary.TundManagement.item29') }}</text>
+          <text style="color: #eb3f57; font-weight: 500">{{ t('Documentary.item3') }}</text>
+        </view>
+        
+        <uni-row :gutter="20" class="responsive-row">
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Label.TradingAccount')" name="dealLogin">
+              <cwg-combox
+                v-model:value="formData.dealLogin"
+                :options="loginOptionsData"
+                :placeholder="t('placeholder.choose')"
+                @change="selectLogin"
+              />
+            </uni-forms-item>
+          </uni-col>
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Label.PlatformType')" name="platform">
+              <cwg-combox disabled v-model:value="formData.platform" :options="[{text: 'MT4', value: 'MT4'}, {text: 'MT5', value: 'MT5'}]" :placeholder="t('placeholder.choose')" />
+            </uni-forms-item>
+          </uni-col>
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Label.AccountType')" name="loginType">
+              <cwg-combox disabled v-model:value="formData.loginType" :options="accountTypeOptions" :placeholder="t('placeholder.choose')" />
+            </uni-forms-item>
+          </uni-col>
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Label.Leverage')" name="leverage">
+              <uni-easyinput disabled v-model="formData.leverage" :placeholder="t('placeholder.input')" />
+            </uni-forms-item>
+          </uni-col>
+        </uni-row>
+
+        <!-- 展示设置 -->
+        <view class="fllow-title">
+          <text>{{ t('Documentary.TundManagement.item32') }}</text>
+        </view>
+        <uni-row :gutter="20" class="responsive-row">
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Documentary.TundManagement.item34')" name="historyShow">
+              <cwg-combox v-model:value="formData.historyShow" :options="[{text: t('Btn.item6'), value: 1}, {text: t('Btn.item7'), value: 0}]" :placeholder="t('placeholder.choose')" />
+            </uni-forms-item>
+          </uni-col>
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Documentary.TundManagement.item35')" name="historyTime">
+              <uni-datetime-picker type="date" v-model="formData.historyTime" :placeholder="t('Documentary.TundManagement.item37')" />
+            </uni-forms-item>
+          </uni-col>
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Documentary.TundManagement.item36')" name="introduceShow">
+              <cwg-combox v-model:value="formData.introduceShow" :options="[{text: t('Btn.item6'), value: 1}, {text: t('Btn.item7'), value: 0}]" :placeholder="t('placeholder.choose')" />
+            </uni-forms-item>
+          </uni-col>
+        </uni-row>
+
+        <!-- 分润设置 -->
+        <view class="fllow-title">
+          <text>{{ t('Documentary.TundManagement.item33') }}</text>
+        </view>
+        <uni-row :gutter="20" class="responsive-row">
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Documentary.TundManagement.item38')" name="distributionType">
+              <cwg-combox v-model:value="formData.distributionType" :options="[{text: t('Documentary.TundManagement.item59'), value: 1}]" :placeholder="t('placeholder.choose')" />
+            </uni-forms-item>
+          </uni-col>
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Documentary.TundManagement.item39')" name="distributionRatio">
+              <view style="display: flex; align-items: center;">
+                <uni-easyinput v-model="formData.distributionRatio" :placeholder="t('placeholder.input')" style="flex: 1;" />
+                <text style="margin-left: 5px;">(%)</text>
+              </view>
+            </uni-forms-item>
+          </uni-col>
+          <uni-col :xs="24" :sm="12">
+            <uni-forms-item :label="t('Documentary.tradingCenter.item32')" name="settlementCycle">
+              <cwg-combox v-model:value="formData.settlementCycle" :options="[{text: '7', value: '7'}, {text: '15', value: '15'}, {text: '30', value: '30'}]" :placeholder="t('placeholder.choose')" />
+            </uni-forms-item>
+          </uni-col>
+        </uni-row>
+
+        <!-- 协议 -->
+        <uni-forms-item class="agree" name="agree">
+          <checkbox-group @change="onAgreeChange">
+            <label class="checkbox">
+              <checkbox value="1" :checked="!!formData.agree" />
+              <view style="display: inline; font-size: 12px;">
+                <text class="crm-cursor">{{ t('Documentary.TundManagement.item42') }}</text>
+                <cwg-link type="pdf" title="Documentary.TundManagement.item43" v-if="['cn', 'zhHant'].indexOf(local) != -1" style="color: #4497ff; display: inline-block; margin: 0 4px;" url="pdf/CopyTradeUserAgreementcn.pdf" target="_blank" />
+                <cwg-link type="pdf" style="color: #4497ff; display: inline-block; margin: 0 4px;" title="Documentary.TundManagement.item43" url="pdf/CopyTradeUserAgreement.pdf" target="_blank" v-else />
+                <text>{{ t('Documentary.TundManagement.item42_2') }}</text>
+              </view>
+            </label>
+          </checkbox-group>
+        </uni-forms-item>
+      </uni-forms>
+    </view>
+  </cwg-popup>
+</template>
+
+<script setup>
+import { ref, computed, watch } from 'vue'
+import { useI18n } from 'vue-i18n'
+import Config from '@/config/index'
+import { documentaryApi } from '@/service/documentary'
+
+const props = defineProps({
+  visible: { type: Boolean, default: false },
+  loginOptions: { type: Array, default: () => [] }
+})
+
+const emit = defineEmits(['close', 'confirm'])
+const { t, locale } = useI18n()
+const { Code } = Config
+const local = locale.value
+
+const formRef = ref(null)
+const formData = ref({
+  nickname: '',
+  personalSignature: '',
+  traderStrategy: '',
+  dealLogin: '',
+  platform: '',
+  loginType: '',
+  leverage: '',
+  historyShow: '',
+  historyTime: '',
+  introduceShow: '',
+  distributionType: '',
+  distributionRatio: '',
+  settlementCycle: '',
+  agree: false
+})
+const isSubmitting = ref(false)
+
+const accountTypeOptions = [
+  { text: t('AccountType.ClassicAccount'), value: 1 },
+  { text: t('AccountType.SeniorAccount'), value: 2 },
+  { text: t('AccountType.SpeedAccount'), value: 5 },
+  { text: t('AccountType.SpeedAccount'), value: 6 },
+  { text: t('AccountType.StandardAccount'), value: 7 },
+  { text: t('AccountType.CentAccount'), value: 8 }
+]
+
+const loginOptionsData = computed(() => {
+  return props.loginOptions.map(item => ({
+    text: `${item.login} - ${groupTypeName(item.type)} - ${t('Custom.Deposit.AvailableBalance')}${groupCurrency(item.currency)}${item.balance}`,
+    value: item.login,
+    disabled: item.balance < 200
+  }))
+})
+
+const groupTypeName = (type) => {
+  const option = accountTypeOptions.find(opt => opt.value == type)
+  return option ? option.text : type
+}
+
+const groupCurrency = (currency) => {
+  return currency ? ` (${currency}) ` : ' '
+}
+
+const rules = computed(() => ({
+  nickname: {
+    rules: [
+      { required: true, errorMessage: t('vaildate.input.empty') },
+      { pattern: /^[0-9a-zA-Z]{1,24}$/, errorMessage: t('Msg.nickname') }
+    ]
+  },
+  personalSignature: {
+    rules: [
+      { required: true, errorMessage: t('vaildate.input.empty') },
+      { pattern: /^[\u4e00-\u9fa5a-zA-Z\s]*$/, errorMessage: t('Documentary.console.item38') }
+    ]
+  },
+  traderStrategy: {
+    rules: [
+      { required: true, errorMessage: t('vaildate.input.empty') },
+      { pattern: /^[\u4e00-\u9fa5a-zA-Z\s]*$/, errorMessage: t('Documentary.console.item38') }
+    ]
+  },
+  dealLogin: { rules: [{ required: true, errorMessage: t('vaildate.select.empty') }] },
+  historyShow: { rules: [{ required: true, errorMessage: t('vaildate.select.empty') }] },
+  historyTime: { rules: [{ required: true, errorMessage: t('vaildate.select.empty') }] },
+  introduceShow: { rules: [{ required: true, errorMessage: t('vaildate.select.empty') }] },
+  distributionType: { rules: [{ required: true, errorMessage: t('vaildate.select.empty') }] },
+  distributionRatio: {
+    rules: [
+      { required: true, errorMessage: t('vaildate.input.empty') },
+      {
+        validateFunction: (rule, value, data, callback) => {
+          if (value >= 0 && value <= 50) return true
+          callback('0-50')
+        }
+      }
+    ]
+  },
+  settlementCycle: { rules: [{ required: true, errorMessage: t('vaildate.select.empty') }] },
+  agree: {
+    rules: [
+      {
+        validateFunction: (rule, value, data, callback) => {
+          if (value) return true
+          callback(t('vaildate.agree.empty'))
+        }
+      }
+    ]
+  }
+}))
+
+const filterChineseEnglishOnly = (field, value) => {
+  if (!value) return
+  const filtered = value.replace(/[^\u4e00-\u9fa5a-zA-Z\s]/g, '')
+  formData.value[field] = filtered
+}
+
+const selectLogin = (val) => {
+  const selected = props.loginOptions.find(item => item.login === val)
+  if (selected) {
+    formData.value.leverage = `1:${selected.leverage}`
+    formData.value.loginType = selected.type
+    formData.value.platform = selected.platform
+  }
+}
+
+const onAgreeChange = (e) => {
+  formData.value.agree = e.detail.value?.includes('1')
+}
+
+const closeDia = () => {
+  emit('close')
+  resetForm()
+}
+
+const confirmDia = async () => {
+  try {
+    await formRef.value.validate()
+  } catch (err) {
+    return
+  }
+
+  if (isSubmitting.value) return
+  isSubmitting.value = true
+
+  try {
+    const res = await documentaryApi.followDealApply({
+      ...formData.value
+    })
+    if (res.code === Code.StatusOK) {
+      uni.showToast({ title: t('Msg.Success'), icon: 'none' })
+      emit('confirm')
+      closeDia()
+    } else {
+      uni.showToast({ title: res.msg, icon: 'none' })
+    }
+  } catch (e) {
+    uni.showToast({ title: t('Msg.Fail'), icon: 'none' })
+  } finally {
+    isSubmitting.value = false
+  }
+}
+
+const resetForm = () => {
+  formRef.value?.clearValidate()
+  formData.value = {
+    nickname: '',
+    personalSignature: '',
+    traderStrategy: '',
+    dealLogin: '',
+    platform: '',
+    loginType: '',
+    leverage: '',
+    historyShow: '',
+    historyTime: '',
+    introduceShow: '',
+    distributionType: '',
+    distributionRatio: '',
+    settlementCycle: '',
+    agree: false
+  }
+}
+
+watch(() => props.visible, (val) => {
+  if (val && props.loginOptions?.length) {
+    // 自动填充默认值,与原版保持一致
+    const defaultOption = props.loginOptions.find(item => item.balance >= 200)
+    if (defaultOption) {
+      formData.value.dealLogin = defaultOption.login
+      selectLogin(defaultOption.login)
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+@import "@/uni.scss";
+
+.crm-form {
+  padding: 0 10px;
+  :deep(.uni-forms-item) {
+    margin-bottom: px2rpx(16);
+  }
+  :deep(.uni-easyinput__content) {
+    border: 1px solid #dcdfe6 !important;
+    background-color: #fff !important;
+  }
+}
+
+.fllow-title {
+  font-size: 16px;
+  font-weight: bold;
+  color: #333;
+  margin-top: 15px;
+  margin-bottom: 10px;
+  display: flex;
+  align-items: center;
+}
+
+/* 移动端排版优化 */
+@media screen and (max-width: 768px) {
+  .responsive-row {
+    display: flex;
+    flex-direction: column;
+    
+    .uni-col {
+      width: 100% !important;
+      padding-left: 0 !important;
+      padding-right: 0 !important;
+    }
+  }
+}
+
+.checkbox {
+  display: flex;
+  align-items: center;
+}
+</style>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 316 - 846
pages/follow/index.vue


+ 248 - 363
pages/follow/report.vue

@@ -1,29 +1,16 @@
 <template>
     <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
-        <cwg-header :title="t('Documentary.TundManagement.item10')" />
+        <cwg-header :title="t('Home.page_ib.item3')" />
         <view class="info-card">
             <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
                 @reset="handleReset" />
-            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="search" :api="listApi"
-                :show-operation="false">
-                <!-- 状态列自定义渲染 -->
-                <template #status="{ row }">
-                    <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">
-                        {{ getStatusText(row) }}
+            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="true" :queryParams="queryParams" :api="listApi"
+                :show-operation="false" :showSummary="true" :summaryMethod="getSummaries">
+                <template #equity="{ row }">
+                    <view class="equity-cell">
+                        <view class="start-equity">{{ formatNumberAll(row.startEquity || '0') }}</view>
+                        <view class="end-equity">{{ formatNumberAll(row.endEquity || '0') }}</view>
                     </view>
-                    <view v-else></view>
-                </template>
-                <!-- 账户类型列自定义渲染 -->
-                <template #accountType="{ row }">
-                    {{ getAccountTypeText(row.type || row.loginType) }}
-                </template>
-                <!-- 金额列格式化 -->
-                <template #amount="{ row }">
-                    <view>-{{ formatNumber(row.withdrawAmount || row.amount) }}</view>
-                </template>
-                <!-- 备注列格式化 -->
-                <template #note="{ row }">
-                    <view>{{ formatNote(row.approveDesc) }}</view>
                 </template>
             </cwg-tabel>
         </view>
@@ -31,373 +18,271 @@
 </template>
 
 <script setup lang="ts">
-import { computed, ref, nextTick } from 'vue';
+import { computed, ref, nextTick, onMounted } from 'vue';
 import { useI18n } from 'vue-i18n';
-const { t, locale } = useI18n();
-import { customApi } from '@/service/custom';
-import useUserStore from "@/stores/use-user-store";
-const userStore = useUserStore();
-const userInfo = computed(() => userStore.userInfo);
-const search = ref({
-    type: 1
-})
-const getInfoAgentTransfer = computed(() => userInfo.value.customInfo?.agentTransfer)
-const typeMap = computed(() => ([
-    { value: 1, text: t('Custom.Recording.NewAccount') },
-    { value: 2, text: t('Custom.Recording.LeverageApply') },
-    { value: 3, text: t('Custom.Recording.InternalTransfer') },
-    { value: 4, text: t('Custom.Recording.ActivitiesApply') },
-    ...(getInfoAgentTransfer.value == 1 ? [{ value: 5, text: t('Home.page_ib.item9') }] : [])
-]));
-const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
+import { documentaryApi } from '@/service/documentary';
+import { ibApi } from '@/service/ib';
 
-// 账户类型映射
-const accountTypeMap = {
-    1: 'AccountType.ClassicAccount',
-    2: 'AccountType.SeniorAccount',
-    5: 'AccountType.SpeedAccount',
-    6: 'AccountType.SpeedAccount',
-    7: 'AccountType.StandardAccount',
-    8: 'AccountType.CentAccount'
-}
-// 拒绝原因映射(示例)
-const reasons = ref({})
-// 根据类型获取列配置
-const getColumnsByType = (type: number) => {
-    switch (type) {
-        case 1: // 开户记录
-            return [
-                {
-                    prop: 'platform',
-                    label: t('Custom.Recording.Platform'),
-                    align: 'left'
-                },
-                {
-                    prop: 'type',
-                    label: t('Custom.PaymentHistory.payType'),
-                    align: 'left',
-                    slot: 'accountType'
-                },
-                {
-                    prop: 'withdrawCurrency',
-                    label: t('Custom.Recording.CurrencyType'),
-                    align: 'left',
-                    formatter: ({ row }) => row.currency || '--',
-                },
-                {
-                    prop: 'leverage',
-                    label: t('Custom.Recording.Lever'),
-                    formatter: ({ row }) => `1: ${row.leverage}` || '--',
-                    align: 'left'
-                },
-                {
-                    prop: 'addTime',
-                    label: t('Custom.PaymentHistory.ApplicationDate'),
-                    type: 'date',
-                    dateFormat: 'YYYY-MM-DD HH:mm',
-                    align: 'left'
-                },
-                {
-                    prop: 'status',
-                    label: t('Custom.Recording.Status'),
-                    slot: 'status',
-                    align: 'left'
-                },
-                {
-                    prop: 'note',
-                    label: t('Custom.Recording.Note'),
-                    type: 'note',
-                    align: 'left'
-                }
-            ]
-        case 2: // 杠杆修改记录
-            return [
-                {
-                    prop: 'login',
-                    label: t('Custom.Recording.TradingAccount'),
-                    align: 'left',
-                    formatter: ({ row }) => row.login || '--'
-                },
-                {
-                    prop: 'oldLeverage',
-                    label: t('Custom.Recording.OldLever'),
-                    align: 'left',
-                    formatter: ({ row }) => row.oldLeverage ? `1:${row.oldLeverage}` : '--'
-                },
-                {
-                    prop: 'newLeverage',
-                    label: t('Custom.Recording.NewLever'),
-                    align: 'left',
-                    formatter: ({ row }) => row.newLeverage ? `1:${row.newLeverage}` : '--'
-                },
-                {
-                    prop: 'addTime',
-                    label: t('Custom.PaymentHistory.ApplicationDate'),
-                    type: 'date',
-                    dateFormat: 'YYYY-MM-DD HH:mm',
-                    align: 'left'
-                },
-                {
-                    prop: 'status',
-                    label: t('Custom.PaymentHistory.Status'),
-                    slot: 'status',
-                    align: 'left'
-                },
-                {
-                    prop: 'note',
-                    label: t('Custom.Recording.Note'),
-                    type: 'note',
-                    align: 'left'
-                }
-            ]
-        case 3: // 转账记录
-        case 5: // 内部转账记录
-            return [
-                {
-                    prop: 'withdrawLogin',
-                    label: t('Custom.Recording.TransferAccounts'),
-                    align: 'left',
-                    formatter: ({ row }) => row.withdrawLogin || '--'
-                },
-                {
-                    prop: 'depositLogin',
-                    label: t('Custom.Recording.IntoAccount'),
-                    align: 'left',
-                    formatter: ({ row }) => row.depositLogin || '--',
-                },
-                {
-                    prop: 'withdrawCurrency',
-                    label: t('Custom.Recording.CurrencyType'),
-                    align: 'left',
-                    formatter: ({ row }) => row.withdrawCurrency || '--',
-                },
-                {
-                    prop: 'withdrawAmount',
-                    label: t('Custom.Recording.Amount'),
-                    align: 'left',
-                    slot: 'amount'
-                },
-                {
-                    prop: 'addTime',
-                    label: t('Custom.PaymentHistory.ApplicationDate'),
-                    type: 'date',
-                    dateFormat: 'YYYY-MM-DD HH:mm',
-                    align: 'left'
-                },
-                {
-                    prop: 'status',
-                    label: t('Custom.PaymentHistory.Status'),
-                    slot: 'status',
-                    align: 'left'
-                },
-                {
-                    prop: 'note',
-                    label: t('Custom.Recording.Note'),
-                    type: 'note',
-                    align: 'left'
-                }
-            ]
-        case 4: // 活动申请记录
-            return [
-                {
-                    prop: 'login',
-                    label: t('Custom.Recording.TradingAccount'),
-                    align: 'left',
-                    formatter: ({ row }) => row.login || '--'
-                },
-                {
-                    prop: 'loginType',
-                    label: t('Custom.Recording.AccountType'),
-                    align: 'left',
-                    slot: 'accountType'
-                },
-                {
-                    prop: 'title',
-                    label: t('Custom.Recording.ActivityName'),
-                    align: 'left',
-                    formatter: ({ row }) => row.title || '--'
-                },
-                {
-                    prop: 'addTime',
-                    label: t('Custom.PaymentHistory.ApplicationDate'),
-                    type: 'date',
-                    dateFormat: 'YYYY-MM-DD HH:mm',
-                    align: 'left'
-                },
-                {
-                    prop: 'status',
-                    label: t('Custom.PaymentHistory.Status'),
-                    slot: 'status',
-                    align: 'left'
-                },
-                {
-                    prop: 'note',
-                    label: t('Custom.Recording.Note'),
-                    type: 'note',
-                    align: 'left'
+const { t } = useI18n();
+
+// 搜索参数
+const searchParams = ref({
+    type: 1,
+    agentId: '',
+    followLogin: '',
+    dealLogin: '',
+    date: []
+});
+
+// 处理传给表格的参数
+const queryParams = computed(() => {
+    // 代理选择处理:picker组件可能是数组也可能是字符串
+    let finalAgentId = '';
+    if (Array.isArray(searchParams.value.agentId)) {
+        finalAgentId = searchParams.value.agentId[searchParams.value.agentId.length - 1];
+    } else {
+        finalAgentId = searchParams.value.agentId;
+    }
+    
+    return {
+        type: searchParams.value.type,
+        agentId: finalAgentId || '',
+        followLogin: searchParams.value.followLogin || '',
+        dealLogin: searchParams.value.dealLogin || '',
+        date: searchParams.value.date || []
+    };
+});
+
+const agentIdOptions = ref<any[]>([
+    { value: 0, text: t('news_add_field.IbReport.ALL') },
+    { value: -1, text: t('news_add_field.IbReport.DirectlyUnder') }
+]);
+
+const handleNodeClick = async (node: any) => {
+    const findNode = (list: any[], val: any): any => {
+        for (let i = 0; i < list.length; i++) {
+            if (list[i].value === val) return list[i];
+            if (list[i].children && list[i].children.length > 0) {
+                const found = findNode(list[i].children, val);
+                if (found) return found;
+            }
+        }
+        return null;
+    };
+    
+    const target = findNode(agentIdOptions.value, node.value);
+    if (target && target.children && target.children.length === 0) {
+        try {
+            const res = await ibApi.ibTree({ pid: node.value });
+            if (res.code === 200 && res.data) {
+                res.data.forEach((item: any) => {
+                    if (item.ibNo) {
+                        target.children.push({
+                            value: item.id,
+                            text: `${item.ibNo}${item.name ? ' - ' + item.name : ''}`,
+                            children: []
+                        });
+                    }
+                });
+            }
+        } catch (e) {
+            console.error(e);
+        }
+    }
+};
+
+const searchIbTree = async () => {
+    try {
+        const res = await ibApi.ibTree({ pid: 0 });
+        if (res.code === 200 && res.data) {
+            res.data.forEach((item: any) => {
+                if (item.ibNo) {
+                    agentIdOptions.value.push({
+                        value: item.id,
+                        text: `${item.ibNo}${item.name ? ' - ' + item.name : ''}`,
+                        children: []
+                    });
                 }
-            ]
+            });
+        }
+    } catch (e) {
+        console.error(e);
     }
-}
-// 动态传入筛选字段配置
+};
+
+onMounted(() => {
+    searchIbTree();
+});
+
 const filterFields = computed(() => [
-    { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: 1 },
+    { key: 'dealLogin', type: 'input', placeholder: t('Documentary.TundManagement.item11') },
+    { key: 'followLogin', type: 'input', placeholder: t('Documentary.console.item28') },
+    {
+        key: 'agentId',
+        type: 'picker',
+        label: t('State.All'),
+        options: agentIdOptions.value,
+        map: { value: 'value', text: 'text' },
+        onNodeClick: handleNodeClick
+    },
     { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
-])
-const searchParams = ref({})
-const tableRef = ref(null)
-const handleSearch = (params) => {
-    search.value = params
+]);
+
+const tableRef = ref();
+
+const handleSearch = () => {
     nextTick(() => {
-        tableRef.value.refreshTable()
-    })
-}
+        tableRef.value?.refreshTable();
+    });
+};
 
-const handleReset = (params) => {
-    search.value = params
+const handleReset = () => {
+    searchParams.value = {
+        type: 1,
+        agentId: '',
+        followLogin: '',
+        dealLogin: '',
+        date: []
+    };
     nextTick(() => {
-        tableRef.value.refreshTable()
-    })
-}
-// 当前列配置
-const currentColumns = computed(() => getColumnsByType(search.value.type))
-// 获取状态文本
-const getStatusText = (row: any) => {
-    const status = row.status
-    // 根据不同记录类型处理状态
-    if (search.value.type === 1) {
-        if (status === 1) return t('State.ToBeProcessed')
-        if (status === 2 && row.accountStatus === 2) return t('State.Completed')
-        if (status === 2 && (row.accountStatus === 1 || !row.accountStatus)) return t('State.InTheProcessing')
-        if (status === 3) return t('State.Refused')
-    } else if (search.value.type === 2) {
-        if (status === 1) return t('State.ToBeProcessed')
-        if (status === 2 && row.leverageStatus === 2) return t('State.Completed')
-        if (status === 2 && row.leverageStatus === 1) return t('State.InTheProcessing')
-        if (status === 3) return t('State.Refused')
-    } else if (search.value.type === 3 || search.value.type === 5) {
-        if (status === 1) return t('State.ToBeProcessed')
-        if (status === 2 && row.withdrawStatus === 2 && row.depositStatus === 2) return t('State.Completed')
-        if (status === 2 && (row.withdrawStatus === 1 || row.depositStatus === 1)) return t('State.InTheProcessing')
-        if (status === 3 || row.withdrawStatus === 3 || row.depositStatus === 3) return t('State.Refused')
-    } else {
-        // 活动申请等
-        if (status === 1) return t('State.ToBeProcessed')
-        if (status === 2) return t('State.Completed')
-        if (status === 3) return t('State.Refused')
+        tableRef.value?.refreshTable();
+    });
+};
+
+// 工具方法
+const NumberDesensitization = (value: any) => {
+    if (!value) return '--';
+    const str = String(value);
+    if (str.length > 4) {
+        return str.substring(0, 2) + '***' + str.substring(str.length - 2);
     }
-    return ''
-}
-// 获取状态样式类
-const getStatusClass = (status: number) => {
-    const classMap: Record<number, string> = {
-        1: 'status-pending',
-        2: 'status-success',
-        3: 'status-processing',
-        4: 'status-danger'
+    return str;
+};
+
+const formatNumberAll = (value: any) => {
+    if (value === "***") return "***";
+    if (isNaN(value)) return '0';
+    let value1 = value.toString();
+    const isNegative = value1.indexOf("-") > -1;
+    if (isNegative) value1 = value1.split("-")[1];
+
+    let num = value1.split(".");
+    let interCount = num[0].length;
+    if (interCount < 3) {
+        return isNegative ? "-" + value1 : value1;
     }
-    return classMap[status] || ''
-}
-// 获取账户类型文本
-const getAccountTypeText = (type: number) => {
-    const key = accountTypeMap[type as keyof typeof accountTypeMap]
-    return key ? t(key) : '--'
-}
-// 格式化数字
-const formatNumber = (value: string | number) => {
-    if (!value) return '--'
-    const num = Number(value)
-    return isNaN(num) ? '--' : num.toFixed(2)
-}
-// 格式化备注
-const formatNote = (approveDesc: string) => {
-    if (!approveDesc) return '--'
-    const reason = reasons.value[approveDesc as keyof typeof reasons.value]
-    if (reason) {
-        return isZh.value ? reason.content : reason.enContent
+    let index = 0;
+    let inter = "";
+    for (let i = interCount - 3; i >= 0; i -= 3) {
+        inter = num[0].substr(i, 3) + (inter == "" ? "" : ",") + inter;
+        index = i;
     }
-    return approveDesc
-}
-const listApi = ref(null)
-listApi.value = customApi.CustomRecordAccount
-</script>
-
-<style scoped lang="scss">
-@import "@/uni.scss";
+    if (index > 0) {
+        inter = num[0].substr(0, index) + (inter == "" ? "" : ",") + inter;
+    }
+    const formatted = inter + (num.length == 1 ? "" : "." + num[1]);
+    return isNegative ? "-" + formatted : formatted;
+};
 
-.avatar {
-    width: px2rpx(60);
-    height: px2rpx(60);
-    border-radius: 4px;
-}
+const groupCurrency1 = (type: string) => {
+    if (type === "GBP") return "£";
+    if (type === "USD") return "$";
+    if (type === "EUR") return "€";
+    if (type === "USC") return "¢";
+    return "$";
+};
 
-.content-title {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    font-size: px2rpx(20);
-    font-weight: 500;
+const currentColumns = computed(() => [
+    {
+        prop: 'dealLogin',
+        label: t('Documentary.tradingCenter.item18'),
+        align: 'left',
+        formatter: ({ row }: any) => NumberDesensitization(row.dealLogin)
+    },
+    {
+        prop: 'followLogin',
+        label: t('Documentary.console.item28'),
+        align: 'left',
+        formatter: ({ row }: any) => row.followLogin || '--'
+    },
+    {
+        prop: 'dealCommission',
+        label: t('Documentary.Report.item4'),
+        align: 'left',
+        formatter: ({ row }: any) => formatNumberAll(row.dealCommission || '0')
+    },
+    {
+        prop: 'dealRatio',
+        label: t('Documentary.Report.item5'),
+        align: 'left',
+        formatter: ({ row }: any) => row.dealRatio || '0%'
+    },
+    {
+        prop: 'agentCommission',
+        label: t('Documentary.Report.item8'),
+        align: 'left',
+        formatter: ({ row }: any) => `${groupCurrency1(row.currency)}${formatNumberAll(row.agentCommission || '0')}`
+    },
+    {
+        prop: 'agentRatio',
+        label: t('Documentary.Report.item7'),
+        align: 'left',
+        formatter: ({ row }: any) => row.agentRatio || '0%'
+    },
+    {
+        prop: 'equity',
+        label: t('Documentary.Report.item17') + ' / ' + t('Documentary.Report.item18'),
+        align: 'left',
+        slot: 'equity'
+    }
+]);
 
-    .content-title-btns {
-        margin: px2rpx(8) 0;
+const getSummaries = (param: any) => {
+    const { columns, summaryData } = param;
+    const sums: string[] = [];
+    if (!summaryData || Object.keys(summaryData).length === 0) return sums;
 
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        gap: px2rpx(12);
+    columns.forEach((column: any, index: number) => {
+        if (index === 0) {
+            sums[index] = t('Label.Total');
+            return;
+        }
 
-        .btn-primary {
-            min-width: px2rpx(120);
-            background-color: var(--color-error);
-            color: white;
-            padding: 0 px2rpx(12);
-            border: none;
-            font-size: px2rpx(14);
-            text-align: center;
-            cursor: pointer;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            gap: px2rpx(8);
+        const prop = column.prop;
+        if (!prop) {
+            sums[index] = '';
+            return;
         }
 
-        .btn-primary:active {
-            background-color: #cf1322;
-            ;
+        if (['dealCommission', 'agentCommission'].includes(prop)) {
+            sums[index] = summaryData[prop] ? formatNumberAll(summaryData[prop]) : '0';
+        } else {
+            sums[index] = '';
         }
-    }
-}
+    });
 
-.operation-btn {
-    :deep(span) {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        gap: px2rpx(4);
-        cursor: pointer;
-        background-color: var(--color-slate-150);
-        padding: px2rpx(8) 0;
-    }
-}
+    return sums;
+};
 
-.operation-btn.disabled {
-    cursor: not-allowed;
-    opacity: 0.5;
-}
+const listApi = ref(documentaryApi.followReportCommission);
+</script>
 
-.search-bar {
-    display: flex;
-    align-items: center;
-    justify-content: flex-start;
-    flex-wrap: wrap;
-    gap: px2rpx(16);
-    margin: px2rpx(16) 0;
+<style scoped lang="scss">
+@import "@/uni.scss";
 
-    .cwg-combox,
-    .uni-easyinput,
-    .uni-date {
-        width: px2rpx(240) !important;
-        flex: none;
+.equity-cell {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    gap: px2rpx(4);
+    
+    .start-equity, .end-equity {
+        font-size: px2rpx(14);
+        line-height: 1.2;
+    }
+    
+    .end-equity {
+        color: var(--color-slate-500);
     }
 }
 </style>

+ 498 - 354
pages/follow/trading-center.vue

@@ -2,402 +2,546 @@
     <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
         <cwg-header :title="t('Documentary.page_doc.item2')" />
         <view class="info-card">
+            <view class="time-header">
+                <text>{{ t('Documentary.console.item2') }}: </text>
+                <text class="time-value">{{ time }}</text>
+            </view>
             <cwg-complex-search :fields="filterFields" v-model="searchParams" @search="handleSearch"
                 @reset="handleReset" />
-            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="false" :queryParams="search" :api="listApi"
-                :show-operation="false">
-                <!-- 状态列自定义渲染 -->
-                <template #status="{ row }">
-                    <view v-if="getStatusText(row)" class="status-tag" :class="getStatusClass(row.status)">
-                        {{ getStatusText(row) }}
-                    </view>
-                    <view v-else></view>
+            <cwg-tabel ref="tableRef" :columns="currentColumns" :immediate="true" :queryParams="queryParams" :api="listApi"
+                :show-operation="false" @sort-change="handleSortChange">
+                
+                <!-- TOP/推荐 -->
+                <template #recommend="{ row }">
+                    <view v-if="row.top == 1" class="recommend-tag top">TOP</view>
+                    <view v-else-if="row.recommend == 1" class="recommend-tag green">{{ t('Documentary.tradingCenter.item26') }}</view>
+                </template>
+
+                <!-- 账户类型 -->
+                <template #groupType="{ row }">
+                    <text v-if="row.groupType == 1">{{ t('AccountType.ClassicAccount') }}</text>
+                    <text v-else-if="row.groupType == 2">{{ t('AccountType.SeniorAccount') }}</text>
+                    <text v-else-if="row.groupType == 5 || row.groupType == 6">{{ t('AccountType.SpeedAccount') }}</text>
+                    <text v-else-if="row.groupType == 7">{{ t('AccountType.StandardAccount') }}</text>
+                    <text v-else-if="row.groupType == 8">{{ t('AccountType.CentAccount') }}</text>
+                    <text v-else>--</text>
                 </template>
-                <!-- 账户类型列自定义渲染 -->
-                <template #accountType="{ row }">
-                    {{ getAccountTypeText(row.type || row.loginType) }}
+                
+                <!-- 活跃度 -->
+                <template #activity="{ row }">
+                    <text>{{ getActivityText(row.activity) }}</text>
                 </template>
-                <!-- 金额列格式化 -->
-                <template #amount="{ row }">
-                    <view>-{{ formatNumber(row.withdrawAmount || row.amount) }}</view>
+                
+                <!-- 查看图表 -->
+                <template #view="{ row }">
+                    <view class="action-icon" @click="toView(row)">
+                        <uni-icons type="chart" size="24" color="#4497ff" />
+                    </view>
                 </template>
-                <!-- 备注列格式化 -->
-                <template #note="{ row }">
-                    <view>{{ formatNote(row.approveDesc) }}</view>
+                
+                <!-- 订阅 -->
+                <template #subscribe="{ row }">
+                    <button class="btn-primary" size="mini" @click="toSubscribe(row)">
+                        <!-- <uni-icons type="copy" size="14" color="#fff" /> -->
+                        <text>{{ t('Documentary.tradingCenter.item25') }}</text>
+                    </button>
                 </template>
             </cwg-tabel>
         </view>
+        
+        <!-- 自动跟随设置弹窗 -->
+        <cwg-popup v-model:visible="dialogFllow" type="center" :title="t('Documentary.tradingCenter.item27')" :showFooters="true">
+            <scroll-view scroll-y class="dia-content" style="max-height: 60vh;">
+                <view class="fllow-title">
+                    <text class="title">{{ t('Documentary.tradingCenter.item27') }}-{{ dialogFllowData1.nickname || '--' }}</text>
+                    <text class="time">{{ t('Documentary.tradingCenter.item45') }}: {{ time }}</text>
+                </view>
+                
+                <view class="fllow-info-grid">
+                    <view class="fllow-content"><text class="tit">{{ t('Documentary.tradingCenter.item29') }}</text><text class="con">{{ dialogFllowData1.dealLogin }}</text></view>
+                    <view class="fllow-content"><text class="tit">{{ t('Documentary.console.item3') }}</text><text class="con">{{ dialogFllowData1.dealPlatform || '--' }}</text></view>
+                    <view class="fllow-content"><text class="tit">{{ t('Documentary.console.item7') }}</text><text class="con">{{ dialogFllowData1.dealBalance || '0.00' }}</text></view>
+                    <view class="fllow-content"><text class="tit">{{ t('Label.AccountType') }}</text><text class="con">{{ getAccountTypeText(dialogFllowData1.dealLoginType) }}</text></view>
+                    <view class="fllow-content"><text class="tit">{{ t('Documentary.console.item6') }}</text><text class="con">{{ dialogFllowData1.dealEquity || '0.00' }}</text></view>
+                    <view class="fllow-content"><text class="tit">{{ t('Documentary.tradingCenter.item30') }}</text><text class="con">{{ dialogFllowData1.distributionType == 1 ? t('Documentary.TundManagement.item59') : '--' }}</text></view>
+                    <view class="fllow-content"><text class="tit">{{ t('Label.Credit') }}</text><text class="con">{{ dialogFllowData1.dealCredit || '0.00' }}</text></view>
+                    <view class="fllow-content"><text class="tit">{{ t('Documentary.AgentBackground.item12') }}</text><text class="con">{{ dialogFllowData1.distributionRatio || '0' }}%</text></view>
+                    <view class="fllow-content"><text class="tit">{{ t('Label.Leverage') }}</text><text class="con">1:{{ dialogFllowData1.dealLeverage || '--' }}</text></view>
+                    <view class="fllow-content"><text class="tit">{{ t('Documentary.tradingCenter.item32') }}</text><text class="con">{{ dialogFllowData1.settlementCycle || '--' }}</text></view>
+                </view>
+
+                <view class="fllow-title section-title">
+                    <text class="title">{{ t('Documentary.tradingCenter.item33') }}</text>
+                </view>
+
+                <uni-forms ref="formRef" :modelValue="dialogFllowData" :rules="rules" label-position="top">
+                    <view class="form-grid">
+                        <uni-forms-item :label="t('Documentary.console.item4')" name="followLogin">
+                            <uni-data-select v-model="dialogFllowData.followLogin" :localdata="followLoginOptions" @change="selectLogin"></uni-data-select>
+                        </uni-forms-item>
+                        <uni-forms-item :label="t('Documentary.tradingCenter.item34')" name="leverage">
+                            <uni-easyinput v-model="dialogFllowData.leverage" disabled />
+                        </uni-forms-item>
+                        <uni-forms-item :label="t('Documentary.tradingCenter.item35')" name="followType">
+                            <uni-data-select v-model="dialogFllowData.followType" :localdata="[
+                                { value: 3, text: t('Documentary.tradingCenter.item118') },
+                                { value: 2, text: t('Documentary.tradingCenter.item117') },
+                                { value: 1, text: t('Documentary.tradingCenter.item116') }
+                            ]"></uni-data-select>
+                        </uni-forms-item>
+                        <uni-forms-item v-if="dialogFllowData.followType == 1" :label="t('Documentary.tradingCenter.item119')" name="volume">
+                            <uni-easyinput v-model="dialogFllowData.volume" />
+                        </uni-forms-item>
+                        <uni-forms-item v-if="dialogFllowData.followType == 2" :label="t('Documentary.tradingCenter.item122') + ' (%)'" name="ratio">
+                            <uni-easyinput v-model="dialogFllowData.ratio" />
+                        </uni-forms-item>
+                    </view>
+
+                    <view class="fllow-title section-title">
+                        <text class="title">{{ t('Documentary.tradingCenter.item37') }}</text>
+                        <switch :checked="dialogFllowData.protect === 1" @change="e => dialogFllowData.protect = e.detail.value ? 1 : 0" color="#368FEC" />
+                    </view>
+
+                    <view class="form-grid" v-if="dialogFllowData.protect === 1">
+                        <uni-forms-item :label="t('Documentary.tradingCenter.item38')" name="protectType">
+                            <uni-data-select v-model="dialogFllowData.protectType" :localdata="[
+                                { value: 1, text: t('Documentary.tradingCenter.item120') }
+                            ]"></uni-data-select>
+                        </uni-forms-item>
+                        <uni-forms-item v-if="dialogFllowData.protectType == 1" :label="t('Documentary.tradingCenter.item39') + ' ($)'" name="protectAmount">
+                            <uni-easyinput v-model="dialogFllowData.protectAmount" />
+                        </uni-forms-item>
+                        <uni-forms-item v-if="dialogFllowData.protectType == 2" :label="t('Documentary.tradingCenter.item122') + ' (%)'" name="protectRatio">
+                            <uni-easyinput v-model="dialogFllowData.protectRatio" />
+                        </uni-forms-item>
+                    </view>
+
+                    <view class="terms-desc">
+                        <text>{{ t('Documentary.tradingCenter.item130') }}</text>
+                    </view>
+
+                    <uni-forms-item name="agree">
+                        <label class="agree-label">
+                            <checkbox :checked="dialogFllowData.agree" @click="dialogFllowData.agree = !dialogFllowData.agree" style="transform:scale(0.7)" />
+                            <text>{{ t('Documentary.tradingCenter.item40') }} - </text>
+                            <text class="link" @click="openAgreement">{{ t('Documentary.tradingCenter.item41') }}</text>
+                        </label>
+                    </uni-forms-item>
+                </uni-forms>
+            </scroll-view>
+            <template #footer>
+                <button class="cancel-btn" @click="applyFllowCancel">{{ t('Btn.Cancel') }}</button>
+                <button class="confirm-btn" type="primary" @click="applyFllow">{{ t('Btn.Confirm') }}</button>
+            </template>
+        </cwg-popup>
     </cwg-page-wrapper>
 </template>
 
 <script setup lang="ts">
-import { computed, ref, nextTick } from 'vue';
+import { computed, ref, nextTick, onMounted, onUnmounted } from 'vue';
 import { useI18n } from 'vue-i18n';
-const { t, locale } = useI18n();
-import { customApi } from '@/service/custom';
+import { documentaryApi } from '@/service/documentary';
 import useUserStore from "@/stores/use-user-store";
+
+const { t, locale } = useI18n();
 const userStore = useUserStore();
 const userInfo = computed(() => userStore.userInfo);
-const search = ref({
-    type: 1
-})
-const getInfoAgentTransfer = computed(() => userInfo.value.customInfo?.agentTransfer)
-const typeMap = computed(() => ([
-    { value: 1, text: t('Custom.Recording.NewAccount') },
-    { value: 2, text: t('Custom.Recording.LeverageApply') },
-    { value: 3, text: t('Custom.Recording.InternalTransfer') },
-    { value: 4, text: t('Custom.Recording.ActivitiesApply') },
-    ...(getInfoAgentTransfer.value == 1 ? [{ value: 5, text: t('Home.page_ib.item9') }] : [])
-]));
-const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
-
-// 账户类型映射
-const accountTypeMap = {
-    1: 'AccountType.ClassicAccount',
-    2: 'AccountType.SeniorAccount',
-    5: 'AccountType.SpeedAccount',
-    6: 'AccountType.SpeedAccount',
-    7: 'AccountType.StandardAccount',
-    8: 'AccountType.CentAccount'
-}
-// 拒绝原因映射(示例)
-const reasons = ref({})
-// 根据类型获取列配置
-const getColumnsByType = (type: number) => {
-    switch (type) {
-        case 1: // 开户记录
-            return [
-                {
-                    prop: 'platform',
-                    label: t('Custom.Recording.Platform'),
-                    align: 'left'
-                },
-                {
-                    prop: 'type',
-                    label: t('Custom.PaymentHistory.payType'),
-                    align: 'left',
-                    slot: 'accountType'
-                },
-                {
-                    prop: 'withdrawCurrency',
-                    label: t('Custom.Recording.CurrencyType'),
-                    align: 'left',
-                    formatter: ({ row }) => row.currency || '--',
-                },
-                {
-                    prop: 'leverage',
-                    label: t('Custom.Recording.Lever'),
-                    formatter: ({ row }) => `1: ${row.leverage}` || '--',
-                    align: 'left'
-                },
-                {
-                    prop: 'addTime',
-                    label: t('Custom.PaymentHistory.ApplicationDate'),
-                    type: 'date',
-                    dateFormat: 'YYYY-MM-DD HH:mm',
-                    align: 'left'
-                },
-                {
-                    prop: 'status',
-                    label: t('Custom.Recording.Status'),
-                    slot: 'status',
-                    align: 'left'
-                },
-                {
-                    prop: 'note',
-                    label: t('Custom.Recording.Note'),
-                    type: 'note',
-                    align: 'left'
-                }
-            ]
-        case 2: // 杠杆修改记录
-            return [
-                {
-                    prop: 'login',
-                    label: t('Custom.Recording.TradingAccount'),
-                    align: 'left',
-                    formatter: ({ row }) => row.login || '--'
-                },
-                {
-                    prop: 'oldLeverage',
-                    label: t('Custom.Recording.OldLever'),
-                    align: 'left',
-                    formatter: ({ row }) => row.oldLeverage ? `1:${row.oldLeverage}` : '--'
-                },
-                {
-                    prop: 'newLeverage',
-                    label: t('Custom.Recording.NewLever'),
-                    align: 'left',
-                    formatter: ({ row }) => row.newLeverage ? `1:${row.newLeverage}` : '--'
-                },
-                {
-                    prop: 'addTime',
-                    label: t('Custom.PaymentHistory.ApplicationDate'),
-                    type: 'date',
-                    dateFormat: 'YYYY-MM-DD HH:mm',
-                    align: 'left'
-                },
-                {
-                    prop: 'status',
-                    label: t('Custom.PaymentHistory.Status'),
-                    slot: 'status',
-                    align: 'left'
-                },
-                {
-                    prop: 'note',
-                    label: t('Custom.Recording.Note'),
-                    type: 'note',
-                    align: 'left'
-                }
-            ]
-        case 3: // 转账记录
-        case 5: // 内部转账记录
-            return [
-                {
-                    prop: 'withdrawLogin',
-                    label: t('Custom.Recording.TransferAccounts'),
-                    align: 'left',
-                    formatter: ({ row }) => row.withdrawLogin || '--'
-                },
-                {
-                    prop: 'depositLogin',
-                    label: t('Custom.Recording.IntoAccount'),
-                    align: 'left',
-                    formatter: ({ row }) => row.depositLogin || '--',
-                },
-                {
-                    prop: 'withdrawCurrency',
-                    label: t('Custom.Recording.CurrencyType'),
-                    align: 'left',
-                    formatter: ({ row }) => row.withdrawCurrency || '--',
-                },
-                {
-                    prop: 'withdrawAmount',
-                    label: t('Custom.Recording.Amount'),
-                    align: 'left',
-                    slot: 'amount'
-                },
-                {
-                    prop: 'addTime',
-                    label: t('Custom.PaymentHistory.ApplicationDate'),
-                    type: 'date',
-                    dateFormat: 'YYYY-MM-DD HH:mm',
-                    align: 'left'
-                },
-                {
-                    prop: 'status',
-                    label: t('Custom.PaymentHistory.Status'),
-                    slot: 'status',
-                    align: 'left'
-                },
-                {
-                    prop: 'note',
-                    label: t('Custom.Recording.Note'),
-                    type: 'note',
-                    align: 'left'
-                }
-            ]
-        case 4: // 活动申请记录
-            return [
-                {
-                    prop: 'login',
-                    label: t('Custom.Recording.TradingAccount'),
-                    align: 'left',
-                    formatter: ({ row }) => row.login || '--'
-                },
-                {
-                    prop: 'loginType',
-                    label: t('Custom.Recording.AccountType'),
-                    align: 'left',
-                    slot: 'accountType'
-                },
-                {
-                    prop: 'title',
-                    label: t('Custom.Recording.ActivityName'),
-                    align: 'left',
-                    formatter: ({ row }) => row.title || '--'
-                },
-                {
-                    prop: 'addTime',
-                    label: t('Custom.PaymentHistory.ApplicationDate'),
-                    type: 'date',
-                    dateFormat: 'YYYY-MM-DD HH:mm',
-                    align: 'left'
-                },
-                {
-                    prop: 'status',
-                    label: t('Custom.PaymentHistory.Status'),
-                    slot: 'status',
-                    align: 'left'
-                },
-                {
-                    prop: 'note',
-                    label: t('Custom.Recording.Note'),
-                    type: 'note',
-                    align: 'left'
-                }
-            ]
+
+// --- Time fetching ---
+const time = ref('');
+let timer: any = null;
+
+const getLocalTime = () => {
+    let timezone = 2; 
+    let offset_GMT = new Date().getTimezoneOffset(); 
+    let nowDate = new Date().getTime(); 
+    let now = new Date(nowDate + offset_GMT * 60 * 1000 + timezone * 60 * 60 * 1000);
+    let year = now.getFullYear();
+    let month = String(now.getMonth() + 1).padStart(2, '0');
+    let day = String(now.getDate()).padStart(2, '0');
+    let hh = String(now.getHours()).padStart(2, '0');
+    let mm = String(now.getMinutes()).padStart(2, '0');
+    time.value = `${year}/${month}/${day}  ${hh}:${mm}`;
+};
+
+const getDate = async () => {
+    try {
+        let res = await documentaryApi.followDealSignalRefreshDate();
+        if (res.code === 200 && res.data) {
+            time.value = res.data;
+        } else {
+            getLocalTime();
+        }
+    } catch (error) {
+        getLocalTime();
     }
-}
-// 动态传入筛选字段配置
+};
+
+onMounted(() => {
+    getDate();
+    timer = setInterval(getDate, 60000); // refresh time
+});
+
+onUnmounted(() => {
+    if (timer) clearInterval(timer);
+});
+
+// --- Table Config ---
+const searchParams = ref({
+    nickname: '',
+    plStart: '', plEnd: '',
+    plRateStart: '', plRateEnd: '',
+    volumeStart: '', volumeEnd: '',
+    winRateStart: '', winRateEnd: '',
+    maxDdRateStart: '', maxDdRateEnd: '',
+    activityRange: '',
+    followsStart: '', followsEnd: ''
+});
+
 const filterFields = computed(() => [
-    { key: 'type', type: 'select', label: t('Custom.PaymentHistory.payType'), placeholder: t('placeholder.choose'), options: typeMap.value, defaultValue: 1 },
-    { key: 'date', label: t('placeholder.Start') + ' - ' + t('placeholder.End'), type: 'daterange' }
-])
-const searchParams = ref({})
-const tableRef = ref(null)
-const handleSearch = (params) => {
-    search.value = params
-    nextTick(() => {
-        tableRef.value.refreshTable()
-    })
-}
+    { key: 'nickname', type: 'input', label: t('TradingCenter.item13'), placeholder: t('placeholder.input') },
+    { key: 'plStart', type: 'number', label: t('TradingCenter.item3') + ' (' + t('placeholder.Start') + ')' },
+    { key: 'plEnd', type: 'number', label: t('TradingCenter.item3') + ' (' + t('placeholder.End') + ')' },
+    { key: 'plRateStart', type: 'number', label: t('TradingCenter.item4') + ' (' + t('placeholder.Start') + ')' },
+    { key: 'plRateEnd', type: 'number', label: t('TradingCenter.item4') + ' (' + t('placeholder.End') + ')' },
+    { key: 'volumeStart', type: 'number', label: t('TradingCenter.item5') + ' (' + t('placeholder.Start') + ')' },
+    { key: 'volumeEnd', type: 'number', label: t('TradingCenter.item5') + ' (' + t('placeholder.End') + ')' },
+    { key: 'winRateStart', type: 'number', label: t('TradingCenter.item6') + ' (' + t('placeholder.Start') + ')' },
+    { key: 'winRateEnd', type: 'number', label: t('TradingCenter.item6') + ' (' + t('placeholder.End') + ')' },
+    { key: 'maxDdRateStart', type: 'number', label: t('TradingCenter.item7') + ' (' + t('placeholder.Start') + ')' },
+    { key: 'maxDdRateEnd', type: 'number', label: t('TradingCenter.item7') + ' (' + t('placeholder.End') + ')' },
+    { key: 'activityRange', type: 'select', label: t('TradingCenter.item8'), options: [
+        { value: 'inactive', text: t('activeState.item1') },
+        { value: 'active', text: t('activeState.item2') },
+        { value: 'very_active', text: t('activeState.item3') }
+    ]},
+    { key: 'followsStart', type: 'number', label: t('TradingCenter.item9') + ' (' + t('placeholder.Start') + ')' },
+    { key: 'followsEnd', type: 'number', label: t('TradingCenter.item9') + ' (' + t('placeholder.End') + ')' }
+]);
 
-const handleReset = (params) => {
-    search.value = params
-    nextTick(() => {
-        tableRef.value.refreshTable()
-    })
-}
-// 当前列配置
-const currentColumns = computed(() => getColumnsByType(search.value.type))
-// 获取状态文本
-const getStatusText = (row: any) => {
-    const status = row.status
-    // 根据不同记录类型处理状态
-    if (search.value.type === 1) {
-        if (status === 1) return t('State.ToBeProcessed')
-        if (status === 2 && row.accountStatus === 2) return t('State.Completed')
-        if (status === 2 && (row.accountStatus === 1 || !row.accountStatus)) return t('State.InTheProcessing')
-        if (status === 3) return t('State.Refused')
-    } else if (search.value.type === 2) {
-        if (status === 1) return t('State.ToBeProcessed')
-        if (status === 2 && row.leverageStatus === 2) return t('State.Completed')
-        if (status === 2 && row.leverageStatus === 1) return t('State.InTheProcessing')
-        if (status === 3) return t('State.Refused')
-    } else if (search.value.type === 3 || search.value.type === 5) {
-        if (status === 1) return t('State.ToBeProcessed')
-        if (status === 2 && row.withdrawStatus === 2 && row.depositStatus === 2) return t('State.Completed')
-        if (status === 2 && (row.withdrawStatus === 1 || row.depositStatus === 1)) return t('State.InTheProcessing')
-        if (status === 3 || row.withdrawStatus === 3 || row.depositStatus === 3) return t('State.Refused')
-    } else {
-        // 活动申请等
-        if (status === 1) return t('State.ToBeProcessed')
-        if (status === 2) return t('State.Completed')
-        if (status === 3) return t('State.Refused')
+const sortState = ref({
+    orderColumn: 1, // 1:plRate, 2:pl, 3:volume, 4:winRate, 5:maxDdRate, 6:activity, 7:follows
+    orderType: 1    // 1:asc, 2:desc
+});
+
+const queryParams = computed(() => {
+    const act = searchParams.value.activityRange;
+    return {
+        ...searchParams.value,
+        activityStart: act === 'inactive' ? 0 : act === 'active' ? 0.33 : act === 'very_active' ? 0.66 : null,
+        activityEnd: act === 'inactive' ? 0.33 : act === 'active' ? 0.66 : act === 'very_active' ? 1 : null,
+        orderColumn: sortState.value.orderColumn,
+        orderType: sortState.value.orderType
+    };
+});
+
+const tableRef = ref(null);
+const listApi = ref(documentaryApi.followDealSearchList);
+
+const handleSearch = (params: any) => {
+    searchParams.value = params;
+    nextTick(() => { tableRef.value?.refreshTable(); });
+};
+
+const handleReset = (params: any) => {
+    searchParams.value = params;
+    nextTick(() => { tableRef.value?.refreshTable(); });
+};
+
+const handleSortChange = ({ prop, order }: any) => {
+    const propToColumn: Record<string, number> = {
+        'plRate': 1, 'pl': 2, 'volume': 3, 'winRate': 4, 'maxDdRate': 5, 'activity': 6, 'follows': 7
+    };
+    sortState.value.orderColumn = propToColumn[prop] || 1;
+    sortState.value.orderType = order === 'desc' ? 2 : 1;
+    nextTick(() => { tableRef.value?.refreshTable(); });
+};
+
+const currentColumns = computed(() => [
+    { prop: 'recommend', label: '', slot: 'recommend', align: 'center', width: 60 },
+    { prop: 'nickname', label: t('Documentary.tradingCenter.item1'), align: 'center' },
+    { prop: 'maskLogin', label: t('newLoop.item11'), align: 'center' },
+    { prop: 'pl', label: t('TradingCenter.item3'), align: 'center', sortable: true },
+    { prop: 'volume', label: t('TradingCenter.item5'), align: 'center', sortable: true },
+    { prop: 'follows', label: t('TradingCenter.item9'), align: 'center', sortable: true },
+    { prop: 'groupType', label: t('Label.AccountType'), slot: 'groupType', align: 'center' },
+    { prop: 'plRate', label: t('TradingCenter.item4'), align: 'center', sortable: true },
+    { prop: 'winRate', label: t('TradingCenter.item6'), align: 'center', sortable: true },
+    { prop: 'maxDdRate', label: t('TradingCenter.item7'), align: 'center', sortable: true },
+    { prop: 'activity', label: t('TradingCenter.item8'), slot: 'activity', align: 'center', sortable: true },
+    { prop: 'view', label: t('Documentary.tradingCenter.item23'), slot: 'view', align: 'center', width: 80 },
+    { prop: 'subscribe', label: t('Documentary.tradingCenter.item24'), slot: 'subscribe', align: 'center', width: 100 }
+]);
+
+const getAccountTypeText = (type: number) => {
+    const accountTypeMap: Record<number, string> = {
+        1: 'AccountType.ClassicAccount',
+        2: 'AccountType.SeniorAccount',
+        3: 'AccountType.AgencyAccount',
+        5: 'AccountType.SpeedAccount',
+        6: 'AccountType.SpeedAccount',
+        7: 'AccountType.StandardAccount',
+        8: 'AccountType.CentAccount'
+    };
+    return type && accountTypeMap[type] ? t(accountTypeMap[type]) : '--';
+};
+
+const getActivityText = (activity: number) => {
+    if (!activity) return "--";
+    switch (activity) {
+        case 1: return t("activeState.item1");
+        case 2: return t("activeState.item2");
+        case 3: return t("activeState.item3");
+        default: return activity;
     }
-    return ''
-}
-// 获取状态样式类
-const getStatusClass = (status: number) => {
-    const classMap: Record<number, string> = {
-        1: 'status-pending',
-        2: 'status-success',
-        3: 'status-processing',
-        4: 'status-danger'
+};
+
+const toView = (row: any) => {
+    uni.navigateTo({
+        url: `/pages/follow/trading-center-single?dealLogin=${row.dealLogin}&id=${row.id}&login=${row.login}`
+    });
+};
+
+// --- Follow Subscription ---
+const dialogFllow = ref(false);
+const dialogFllowData1 = ref<any>({});
+const dialogFllowLoginData = ref<any[]>([]);
+const dialogFllowData = ref({
+    followLogin: '',
+    protect: 0,
+    protectType: 1,
+    protectAmount: '',
+    protectRatio: '',
+    followType: 1,
+    volume: '',
+    ratio: '',
+    leverage: '',
+    agree: false
+});
+
+const formRef = ref<any>(null);
+
+const rules = {
+    protectAmount: { rules: [{ required: true, errorMessage: t('vaildate.input.empty') }] },
+    protectRatio: { rules: [{ required: true, errorMessage: t('vaildate.input.empty') }] },
+    volume: { rules: [{ required: true, errorMessage: t('vaildate.input.empty') }] },
+    ratio: { rules: [{ required: true, errorMessage: t('vaildate.input.empty') }] },
+    followLogin: { rules: [{ required: true, errorMessage: t('vaildate.select.empty') }] },
+    followType: { rules: [{ required: true, errorMessage: t('vaildate.select.empty') }] },
+    agree: { rules: [{ required: true, errorMessage: t('vaildate.agree.empty') }] }
+};
+
+const followLoginOptions = computed(() => {
+    return dialogFllowLoginData.value.map(item => ({
+        value: item.login,
+        text: `${item.login} - ${getAccountTypeText(item.loginType)} - ${t('Custom.Deposit.AvailableBalance')}: $${item.balance || 0}`
+    }));
+});
+
+const toSubscribe = async (row: any) => {
+    uni.showLoading({ title: t('State.InTheProcessing') });
+    try {
+        let res = await documentaryApi.followDealSubscribeInfo({ dealId: row.dealId });
+        if (res.code === 200 || res.code === 0 || res.code === 10000) {
+            dialogFllowData1.value = row;
+            dialogFllowData.value.protect = 0;
+            dialogFllowLoginData.value = res.data || [];
+            dialogFllow.value = true;
+        } else {
+            uni.showToast({ title: res.msg || 'Error', icon: 'none' });
+        }
+    } finally {
+        uni.hideLoading();
     }
-    return classMap[status] || ''
-}
-// 获取账户类型文本
-const getAccountTypeText = (type: number) => {
-    const key = accountTypeMap[type as keyof typeof accountTypeMap]
-    return key ? t(key) : '--'
-}
-// 格式化数字
-const formatNumber = (value: string | number) => {
-    if (!value) return '--'
-    const num = Number(value)
-    return isNaN(num) ? '--' : num.toFixed(2)
-}
-// 格式化备注
-const formatNote = (approveDesc: string) => {
-    if (!approveDesc) return '--'
-    const reason = reasons.value[approveDesc as keyof typeof reasons.value]
-    if (reason) {
-        return isZh.value ? reason.content : reason.enContent
+};
+
+const selectLogin = (val: string) => {
+    const target = dialogFllowLoginData.value.find(item => item.login === val);
+    if (target) {
+        dialogFllowData.value.leverage = `1:${target.leverage}`;
     }
-    return approveDesc
-}
-const listApi = ref(null)
-listApi.value = customApi.CustomRecordAccount
+};
+
+const openAgreement = () => {
+    const lang = locale.value;
+    const url = ['cn', 'zhHant'].includes(lang) ? 'pdf/CopyTradeUserAgreementcn.pdf' : 'pdf/CopyTradeUserAgreement.pdf';
+    // Use plus.runtime.openURL or window.open depending on platform
+    // #ifdef H5
+    window.open(url, '_blank');
+    // #endif
+    // #ifndef H5
+    uni.showToast({ title: 'Not supported on this platform', icon: 'none' });
+    // #endif
+};
+
+let submitting = false;
+const applyFllow = async () => {
+    if (submitting) return;
+    if (!dialogFllowData.value.agree) {
+        uni.showToast({ title: t('vaildate.agree.empty'), icon: 'none' });
+        return;
+    }
+    
+    try {
+        await formRef.value?.validate();
+    } catch (e) {
+        return;
+    }
+
+    submitting = true;
+    uni.showLoading({ title: t('State.InTheProcessing') });
+    
+    try {
+        const payload = {
+            dealId: dialogFllowData1.value.id,
+            followLogin: dialogFllowData.value.followLogin,
+            protect: dialogFllowData.value.protect,
+            protectType: dialogFllowData.value.protect ? dialogFllowData.value.protectType : null,
+            protectAmount: dialogFllowData.value.protect ? dialogFllowData.value.protectAmount : null,
+            protectRatio: dialogFllowData.value.protect ? dialogFllowData.value.protectRatio : null,
+            followType: dialogFllowData.value.followType,
+            volume: dialogFllowData.value.followType == 1 ? Number(dialogFllowData.value.volume) * 100 : null,
+            ratio: dialogFllowData.value.followType == 2 ? dialogFllowData.value.ratio : null,
+        };
+        
+        let res = await documentaryApi.followDealSubscriSubscribe(payload);
+        if (res.code === 200 || res.code === 0 || res.code === 10000) {
+            uni.showToast({ title: t('Msg.Success'), icon: 'success' });
+            applyFllowCancel();
+        } else {
+            const msg = res.msg === "EMPTY_POSITION_BEFORE_SUBSCRIBE" ? t("Documentary.tradingCenter.item134") : res.msg;
+            uni.showToast({ title: msg || 'Error', icon: 'none' });
+        }
+    } finally {
+        submitting = false;
+        uni.hideLoading();
+    }
+};
+
+const applyFllowCancel = () => {
+    dialogFllow.value = false;
+    dialogFllowData.value = {
+        followLogin: '', protect: 0, protectType: 1, protectAmount: '', protectRatio: '',
+        followType: 1, volume: '', ratio: '', leverage: '', agree: false
+    };
+};
+
 </script>
 
 <style scoped lang="scss">
 @import "@/uni.scss";
 
-.avatar {
-    width: px2rpx(60);
-    height: px2rpx(60);
+.time-header {
+    display: flex;
+    justify-content: flex-end;
+    font-size: 14px;
+    margin-bottom: 10px;
+    font-weight: 500;
+    color: #333;
+    .time-value {
+        margin-left: 6px;
+        color: #666;
+    }
+}
+
+.recommend-tag {
+    font-size: 12px;
+    color: #fff;
+    padding: 2px 6px;
     border-radius: 4px;
+    display: inline-block;
+    &.top { background-color: #eb3f57; }
+    &.green { background-color: #89be30; }
 }
 
-.content-title {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    font-size: px2rpx(20);
-    font-weight: 500;
+.action-icon {
+    display: inline-flex;
+    cursor: pointer;
+}
 
-    .content-title-btns {
-        margin: px2rpx(8) 0;
+.btn-primary {
+    background-color: #4497ff;
+    color: white;
+    padding: 0 px2rpx(12);
+    border: none;
+    font-size: px2rpx(12);
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    gap: px2rpx(4);
+    height: 28px;
+    line-height: 28px;
+    border-radius: 4px;
+}
 
+.dia-content {
+    padding: 10px;
+    .fllow-title {
         display: flex;
+        justify-content: space-between;
         align-items: center;
-        justify-content: center;
-        gap: px2rpx(12);
-
-        .btn-primary {
-            min-width: px2rpx(120);
-            background-color: var(--color-error);
-            color: white;
-            padding: 0 px2rpx(12);
-            border: none;
-            font-size: px2rpx(14);
-            text-align: center;
-            cursor: pointer;
-            display: flex;
-            align-items: center;
-            justify-content: center;
-            gap: px2rpx(8);
+        border-bottom: 1px solid #eee;
+        padding-bottom: 10px;
+        margin-bottom: 10px;
+        .title {
+            font-weight: bold;
+            color: #333;
+            padding-left: 8px;
+            border-left: 4px solid #eb3f57;
+            font-size: 16px;
         }
-
-        .btn-primary:active {
-            background-color: #cf1322;
-            ;
+        .time {
+            font-size: 12px;
+            color: #888;
+        }
+    }
+    .section-title {
+        margin-top: 20px;
+        .title { border-left-color: #4497ff; }
+    }
+    
+    .fllow-info-grid {
+        display: grid;
+        grid-template-columns: 1fr 1fr;
+        gap: 10px 20px;
+        .fllow-content {
+            display: flex;
+            justify-content: space-between;
+            font-size: 14px;
+            border-bottom: 1px dashed #eee;
+            padding-bottom: 4px;
+            .tit { color: #666; }
+            .con { font-weight: 500; color: #333; }
         }
     }
-}
 
-.operation-btn {
-    :deep(span) {
+    .form-grid {
+        display: grid;
+        grid-template-columns: 1fr 1fr;
+        gap: 10px 20px;
+        margin-top: 10px;
+    }
+    
+    .terms-desc {
+        font-size: 12px;
+        color: #888;
+        margin: 10px 0;
+    }
+    
+    .agree-label {
         display: flex;
         align-items: center;
-        justify-content: center;
-        gap: px2rpx(4);
-        cursor: pointer;
-        background-color: var(--color-slate-150);
-        padding: px2rpx(8) 0;
+        font-size: 14px;
+        color: #333;
+        .link { color: #4497ff; cursor: pointer; }
     }
 }
 
-.operation-btn.disabled {
-    cursor: not-allowed;
-    opacity: 0.5;
-}
-
-.search-bar {
-    display: flex;
-    align-items: center;
-    justify-content: flex-start;
-    flex-wrap: wrap;
-    gap: px2rpx(16);
-    margin: px2rpx(16) 0;
-
-    .cwg-combox,
-    .uni-easyinput,
-    .uni-date {
-        width: px2rpx(240) !important;
-        flex: none;
-    }
+.cancel-btn, .confirm-btn {
+    flex: 1;
+    margin: 0 10px;
+    border-radius: 4px;
 }
+.cancel-btn { background-color: #f5f5f5; color: #333; }
+.confirm-btn { background-color: #4497ff; color: #fff; }
 </style>

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio