Przeglądaj źródła

feat:客户控制台,客户管理列表。

ljc 2 miesięcy temu
rodzic
commit
22e69070a4

+ 7 - 0
components/cwg-sidebar.vue

@@ -73,7 +73,14 @@ const menu = ref<MenuItem[]>(
             path: '/pages/ib/index', label: 'Home.msg.Ib', icon: 'crm-ib',
             children: [
                 { path: '/pages/ib/index', label: 'Home.page_ib.item1', icon: 'icon-client' },
+              //  客户管理
                 { path: '/pages/ib/customer', label: 'Home.page_ib.item2', icon: 'icon-deposit' },
+              //代理管理
+                { path: '/pages/ib/subsList', label: 'Home.page_ib.item12', icon: 'icon-deposit' },
+              // 信号源列表
+                { path: '/pages/ib/agentList', label: 'Home.page_ib.item11', icon: 'icon-deposit' },
+              // 账户管理
+                { path: '/pages/ib/accountList', label: 'Home.page_ib.item10', icon: 'icon-deposit' },
                 { path: '/pages/ib/report', label: 'Home.page_ib.item3', icon: 'icon-withdrawal' },
                 { path: '/pages/ib/transfer', label: 'Home.page_ib.item4', icon: 'icon-payment' },
                 { path: '/pages/ib/withdraw', label: 'Home.page_ib.item5', icon: 'icon-transfer' },

+ 6 - 3
composables/useFilters.ts

@@ -30,8 +30,11 @@ export function useFilters(): Filters {
 
     /**
      * 数字千分位格式化(支持负数)
+     * @param {number | string} value - 要格式化的数字或字符串
+     * @param {boolean} toArray - 是否转化为数组,默认值为false
+     * @returns {string} 格式化后的字符串
      */
-    const numberFormat = (value: number | string): string => {
+    const numberFormat = (value: number | string,toArray:boolean = false): string => {
         if (value == '***') {
             return '***';
         }
@@ -59,7 +62,7 @@ export function useFilters(): Filters {
             if (index > 0) {
                 inter = num[0].substr(0, index) + (inter == '' ? '' : ',') + inter;
             }
-            return '-' + inter + (num.length == 1 ? '' : '.' + num[1]);
+            return toArray ? ['-' + inter,num[1]] : '-' + inter + (num.length == 1 ? '' : '.' + num[1]);
         } else {
             let num = value1.split('.');
             let interCount = num[0].length;
@@ -77,7 +80,7 @@ export function useFilters(): Filters {
             if (index > 0) {
                 inter = num[0].substr(0, index) + (inter == '' ? '' : ',') + inter;
             }
-            return inter + (num.length == 1 ? '' : '.' + num[1]);
+            return toArray ? [inter,num[1]] : inter + (num.length == 1 ? '' : '.' + num[1]);
         }
     };
 

+ 4 - 1
locale/cn.json

@@ -1043,7 +1043,10 @@
       "item6": "活动中心",
       "item7": "申请历史",
       "item8": "佣金模板",
-      "item9": "代理内转"
+      "item9": "代理内转",
+      "item10": "账户管理",
+      "item11": "信号源管理",
+      "item12": "代理管理"
     },
     "page_shop": {
       "item1": "商城首页",

+ 21 - 0
pages.json

@@ -191,6 +191,27 @@
         "navigationStyle": "custom"
       }
     },
+    {
+      "path": "pages/ib/accountList",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/ib/subsList",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/ib/agentList",
+      "style": {
+        "navigationBarTitleText": "",
+        "navigationStyle": "custom"
+      }
+    },
     {
       "path": "pages/ib/linkList",
       "style": {

+ 24 - 0
pages/ib/accountList.vue

@@ -0,0 +1,24 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Home.page_ib.item2')" />
+
+        <view class="account-section">
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
+import { onLoad } from '@dcloudio/uni-app'
+import { useI18n } from 'vue-i18n' // uni-app 中已集成,但需配置
+import { customApi } from '@/service/custom'
+import { financialApi } from '@/service/financial'
+import Config from '@/config/index'
+import AddBankDialog from '@/components/AddBankDialog.vue';
+import PaymentMethodsList from './components/PaymentMethodsList.vue'
+const { t, locale } = useI18n()
+const isZh = computed(() => ['cn', 'zhHant'].includes(locale.value))
+</script>
+<style lang="scss" scoped>
+@import "@/uni.scss";
+</style>

+ 24 - 0
pages/ib/agentList.vue

@@ -0,0 +1,24 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Home.page_ib.item2')" />
+
+        <view class="account-section">
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
+import { onLoad } from '@dcloudio/uni-app'
+import { useI18n } from 'vue-i18n' // uni-app 中已集成,但需配置
+import { customApi } from '@/service/custom'
+import { financialApi } from '@/service/financial'
+import Config from '@/config/index'
+import AddBankDialog from '@/components/AddBankDialog.vue';
+import PaymentMethodsList from './components/PaymentMethodsList.vue'
+const { t, locale } = useI18n()
+const isZh = computed(() => ['cn', 'zhHant'].includes(locale.value))
+</script>
+<style lang="scss" scoped>
+@import "@/uni.scss";
+</style>

+ 267 - 17
pages/ib/customer.vue

@@ -1,23 +1,273 @@
 <template>
-    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
-        <cwg-header :title="t('Home.page_ib.item2')" />
-        <view class="account-section">
+  <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+    <cwg-header :title="t('Home.page_ib.item2')" />
+    <view class="info-card">
+      <view class="search-content">
+        <view class="search-bar">
+          <uni-easyinput v-model="search.cId" placeholder="CID" />
+          <uni-easyinput v-model="search.name" :placeholder="t('Ib.Custom.NameLabel')" />
+          <uni-easyinput v-model="search.email" :placeholder="t('Label.Email')" />
         </view>
-    </cwg-page-wrapper>
+        <view class="search-tabs">
+          <view class="crm-cursor tab-item" :class="{ active: search.belongsType == 1 }" @click="chooseBelongsType(1)"
+          >
+            {{ t('Ib.Custom.Unverified') }}
+            <view
+              v-if="statistics.unverifiedNum !== undefined"
+              class="count-badge"
+            >({{ statistics.unverifiedNum }})
+            </view
+            >
+          </view>
+          <view
+            class="crm-cursor tab-item"
+            :class="{ active: search.belongsType == 2 }"
+            @click="chooseBelongsType(2)"
+          >
+            {{ t('Ib.Custom.UnDeposit') }}
+            <view
+              v-if="statistics.unDepositNum !== undefined"
+              class="count-badge"
+            >({{ statistics.unDepositNum }})
+            </view
+            >
+          </view>
+          <view
+            class="crm-cursor tab-item"
+            :class="{ active: search.belongsType == 3 }"
+            @click="chooseBelongsType(3)"
+          >
+            {{ t('Ib.Custom.Deposited') }}
+            <view
+              v-if="statistics.depositNum !== undefined"
+              class="count-badge"
+            >({{ statistics.depositNum }})
+            </view
+            >
+          </view>
+        </view>
+      </view>
+      <cwg-tabel
+        ref="tableRef"
+        :columns="columns"
+        :mobilePrimaryFields="mobilePrimaryFields"
+        :queryParams="search"
+        :api="listApi"
+        :show-operation="true"
+        :showPagination="true"
+      >
+        <template #action="{ row }">
+          <cwg-dropdown :menu-list="menuList(row)" @menuClick="handleMenuClick">
+            <view class="pc-header-btn">
+              <cwg-icon name="crm-ellipsis" :size="24" />
+            </view>
+          </cwg-dropdown>
+        </template>
+      </cwg-tabel>
+    </view>
+  </cwg-page-wrapper>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
-import { onLoad } from '@dcloudio/uni-app'
-import { useI18n } from 'vue-i18n' // uni-app 中已集成,但需配置
-import { customApi } from '@/service/custom'
-import { financialApi } from '@/service/financial'
-import Config from '@/config/index'
-import AddBankDialog from '@/components/AddBankDialog.vue';
-import PaymentMethodsList from './components/PaymentMethodsList.vue'
-const { t, locale } = useI18n()
-const isZh = computed(() => ['cn', 'zhHant'].includes(locale.value))
+  import { computed, ref, onMounted } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import Config from '@/config/index'
+
+  const { t, locale } = useI18n()
+  import { customApi } from '@/service/custom'
+  import { lang } from '@/composables/config'
+  import { ibApi } from '@/service/ib'
+
+  const { Code } = Config
+
+  const statistics = ref({
+    unverifiedNum: 0,
+    unDepositNum: 0,
+    depositNum: 0,
+  })
+
+  const search = ref({
+    'name': '',
+    'email': '',
+    'cId': '700101',
+    // 1:未实名 2:未入金 3:已入金
+    belongsType: null,
+  })
+  // 表格列配置
+  const columns = ref([
+    {
+      prop: 'cId',
+      label: t('Label.CidAccount'),
+      align: 'center',
+    },
+    {
+      prop: 'name',
+      label: t('Ib.Custom.NameLabel'),
+      align: 'center',
+    },
+    {
+      prop: 'email',
+      label: t('Label.Email'),
+      align: 'center',
+    },
+    {
+      prop: 'countryEnName',
+      label: t('Label.Nationality'),
+      align: 'center',
+      width: lang ? 110 : 0,
+    },
+    {
+      prop: 'addTime',
+      label: t('Label.RegistrationTime'),
+      align: 'center',
+      width: lang ? 110 : 0,
+    },
+    {
+      prop: 'belongsType',
+      label: t('Ib.Custom.CustomerStatus'),
+      align: 'center',
+      formatter: ({ row }) => row.belongsType == 1 ? t('Ib.Custom.Unverified') :
+        row.belongsType == 2 ? t('Ib.Custom.UnDeposit') :
+          row.belongsType == 3 ? t('Ib.Custom.Deposited') : '--',
+    },
+    {
+      prop: 'ibStatus',
+      label: t('Ib.Custom.ApplyAgent'),
+      align: 'center',
+      formatter: ({ row }) => row.ibStatus == 2 ? t('Ib.Custom.Yes') : t('Ib.Custom.No'),
+    },
+    {
+      prop: 'action',
+      label: t('Label.Action'),
+      align: 'center',
+      slot: 'action',
+    },
+  ])
+
+  const mobilePrimaryFields = ref([
+    {
+      prop: 'cId',
+      label: t('Label.CidAccount'),
+      align: 'center',
+    },
+    {
+      prop: 'name',
+      label: t('Ib.Custom.NameLabel'),
+      align: 'center',
+    },
+    {
+      prop: 'email',
+      label: t('Label.Email'),
+      align: 'center',
+    },
+    {
+      prop: 'more',
+      type: 'more',
+      width: 20,
+      align: 'right',
+    },
+  ])
+
+
+  const listApi = ref(null)
+  listApi.value = ibApi.customerSubs
+  //选择belongsType(点击已选中的 tab 可反选取消)
+  const chooseBelongsType = (belongsType) => {
+    if (search.value.belongsType == belongsType) {
+      search.value.belongsType = null // 反选:取消选中
+    } else {
+      search.value.belongsType = belongsType
+    }
+  }
+  // 下拉菜单配置
+  const menuList = (row) => {
+    return [
+      {
+        label: t('Documentary.AgentBackground.item1'),
+        type: 'documentary',
+        show: true,
+      },
+      {
+        label: t('Home.msg.ibTitle'),
+        type: 'applyIb',
+        show: row.ibStatus == 1 && row.belongsType != 1,
+      },
+      {
+        label: t('Ib.Custom.AccountAdjust'),
+        type: 'Point',
+        show: row.belongsType != 1,
+      },
+    ].filter((item) => item.show)
+  }
+
+  //获取统计数
+  const getStatistics = async () => {
+    try {
+      let res = await ibApi.customerSubsStatistics({})
+      if (res.code == Code.StatusOK && res.data) {
+        statistics.value = {
+          unverifiedNum: res.data.unverifiedNum || 0,
+          unDepositNum: res.data.unDepositNum || 0,
+          depositNum: res.data.depositNum || 0,
+        }
+      }
+    } catch (error) {
+    }
+  }
+  onMounted(() => {
+    getStatistics()
+  })
+
+
 </script>
-<style lang="scss" scoped>
-@import "@/uni.scss";
-</style>
+
+<style scoped lang="scss">
+  @import "@/uni.scss";
+
+  .search-content {
+    display: flex;
+    justify-content: space-between;
+  }
+
+
+  .search-bar {
+    display: flex;
+    align-items: center;
+    justify-content: flex-start;
+    flex-wrap: wrap;
+    gap: px2rpx(10);
+    margin: px2rpx(16) 0;
+
+    .cwg-combox,
+    .uni-easyinput,
+    .uni-date {
+      width: px2rpx(180) !important;
+      flex: none;
+    }
+  }
+
+  .search-tabs {
+    display: flex;
+    align-items: center;
+    flex-wrap: wrap;
+    gap: px2rpx(10);
+    margin: px2rpx(16) 0;
+
+    .tab-item {
+      display: flex;
+      min-width: px2rpx(100);
+      border: 1px solid #F0F0F0;
+      border-radius: px2rpx(4);
+      margin-left: px2rpx(5);
+      height: px2rpx(33);
+      line-height: px2rpx(33);
+      justify-content: center;
+
+      &.active {
+        color: var(--color-white);
+        background-color: var(--color-error);
+        border-color: var(--color-error);
+      }
+    }
+  }
+</style>

+ 65 - 130
pages/ib/index.vue

@@ -27,7 +27,7 @@
               </view>
               <view class="total-earnings">
                 <text class="total-label">{{ t('Ib.Index.TotalRevenue') }}</text>
-                <text class="total-value">{{ totalEarnings }}</text>
+                <text class="total-value">{{ ibData.all }}</text>
               </view>
             </view>
           </view>
@@ -45,9 +45,9 @@
                 <button class="link-btn" @click="LinkActivity1">
                   {{ t('Ib.Index.CreateLink') }}
                 </button>
-<!--                <button class="link-btn" @click="LinkActivity">-->
-<!--                  {{ t('Ib.Index.CreateLinkActivity') }}-->
-<!--                </button>-->
+                <!--                <button class="link-btn" @click="LinkActivity">-->
+                <!--                  {{ t('Ib.Index.CreateLinkActivity') }}-->
+                <!--                </button>-->
               </view>
             </view>
           </view>
@@ -81,25 +81,25 @@
             </view>
           </view>
           <!-- 归属推荐码 -->
-<!--          <view class="card code-card">
-            <view class="card-header">
-              <view class="header-left">
-                <text class="header-title">{{ t('Tips.AttributionCode') }}</text>
-                <uni-tooltip placement="top">
-                  <text class="icon-tip">?</text>
-                  <template v-slot:content>
-                    <view style="width: 100px;">
-                      {{ t('Tips.tips') }}
-                    </view>
-                  </template>
-                </uni-tooltip>
-              </view>
-            </view>
-            <view class="code-content">
-              <uni-easyinput class="code-input" :disabled="true" v-model="getInfoId" :clearable="false"></uni-easyinput>
-              <button class="link-btn">{{ t('Ib.Index.Copy') }}</button>
-            </view>
-          </view>-->
+          <!--          <view class="card code-card">
+                      <view class="card-header">
+                        <view class="header-left">
+                          <text class="header-title">{{ t('Tips.AttributionCode') }}</text>
+                          <uni-tooltip placement="top">
+                            <text class="icon-tip">?</text>
+                            <template v-slot:content>
+                              <view style="width: 100px;">
+                                {{ t('Tips.tips') }}
+                              </view>
+                            </template>
+                          </uni-tooltip>
+                        </view>
+                      </view>
+                      <view class="code-content">
+                        <uni-easyinput class="code-input" :disabled="true" v-model="getInfoId" :clearable="false"></uni-easyinput>
+                        <button class="link-btn">{{ t('Ib.Index.Copy') }}</button>
+                      </view>
+                    </view>-->
         </view>
       </uni-col>
     </uni-row>
@@ -193,7 +193,8 @@
       </view>
     </cwg-popup>
     <!-- 活动链接弹窗 -->
-    <cwg-popup ref="linkActivityPopup" type="center" :title="t('Ib.Index.CreateLinkActiv')" :showFooters="false" showFooterLine>
+    <cwg-popup ref="linkActivityPopup" type="center" :title="t('Ib.Index.CreateLinkActiv')" :showFooters="false"
+               showFooterLine>
       <view class="dia-content">
         <view class="content" style="font-size: 14px; text-align: left">
           <view class="label">{{ t('Ib.Index.ChooseActiv') }}</view>
@@ -288,14 +289,14 @@
   import useUserStore from '@/stores/use-user-store'
   import { useStorage } from '@/hooks/useStorage'
   import QrCode from '@/components/QrCode.vue'
+  import { useFilters } from '@/composables/useFilters'
 
   const { t } = useI18n()
   const router = useRouter()
   const { Code } = config
   const { userInfo } = useUserStore()
+  const { numberFormat } = useFilters()
   // 数据
-  const balanceInt = ref(0)
-  const balanceDecimal = ref('00')
   const totalEarnings = ref(0.00)
   const partnerLink = ref('https://one.exnessonelink.com/a/plokue4yj3')
   const partnerCode = ref('PLOKUE4YJ3')
@@ -350,6 +351,13 @@
     return userInfo.ibInfo.id
   })
 
+  const balanceInt = computed(() => {
+    return numberFormat(ibData.value?.balance || 0, true)[0]
+  })
+  const balanceDecimal = computed(() => {
+    return numberFormat(ibData.value?.balance || 0, true)[1]
+  })
+
   // 国家
   const country = computed(() => {
     console.log(userInfo.customInfo.country, '2')
@@ -360,9 +368,9 @@
     excludeShowLoginTypes.value = val
   }
   const isAfterJuly7 = () => {
-    const currentDate = new Date();
-    const july7 = new Date(currentDate.getFullYear(), 6, 7); // 月份从0开始,6表示7月
-    return currentDate >= july7;
+    const currentDate = new Date()
+    const july7 = new Date(currentDate.getFullYear(), 6, 7) // 月份从0开始,6表示7月
+    return currentDate >= july7
   }
 
   const getValidAccountTypes = (selectedExcludeValues, selectedSpreadId) => {
@@ -404,7 +412,7 @@
   const downloadQrCode = (type = 0) => {
     if (type === 1) {
       qrCode1.value.download()
-    }else {
+    } else {
       qrCode.value.download()
     }
   }
@@ -433,7 +441,6 @@
       excludeShowLoginTypes.value,
       selectedSpreadId.value,
     )
-    console.log('validList', validList)
     if (validList.invalidLabels.length > 0) {
       return new Promise((resolve) => {
         uni.showModal({
@@ -523,10 +530,6 @@
   }
   // 生成开户链接
   const LinkActivity1 = async () => {
-    // await loginTypeList()
-    // await getAgentAccountSetting()
-    // selectedSpreadId.value = ''
-    // linkPopup.value.open()
     // 跳转到开户链接页面
     uni.navigateTo({
       url: '/pages/ib/linkList',
@@ -541,12 +544,12 @@
     }
     let res = await ibApi.marketAgentLinkList({})
     if (res.code === Code.StatusOK) {
-      agentLinkList.value = res.data??[]
+      agentLinkList.value = res.data ?? []
       loginTypeList()
       getAgentAccountSetting()
-      activityLing.value = '';
+      activityLing.value = ''
       linkActivity.value = ''
-      commission.value = 0;
+      commission.value = 0
       ibInvalid.value = 'B0'
       linkActivityPopup.value.open()
       flag.value = false
@@ -554,55 +557,55 @@
   }
 
   const CreateActivityLink = async () => {
-    if (!activityLing.value){
-      uni.showToast({title: t("Ib.Index.ChooseActiv"),icon: 'error'})
+    if (!activityLing.value) {
+      uni.showToast({ title: t('Ib.Index.ChooseActiv'), icon: 'error' })
       return
     }
     const linkValue = await getLink1()
     if (!linkValue) return
-    if (activityLing.value.indexOf("http") > -1) {
-      if (activityLing.value.indexOf("?") > -1) {
+    if (activityLing.value.indexOf('http') > -1) {
+      if (activityLing.value.indexOf('?') > -1) {
         linkActivity.value =
           activityLing.value +
-          "&mmdi=" +
+          '&mmdi=' +
           getInfoId.value +
-          "&mmF=" +
+          '&mmF=' +
           linkValue +
-          "&mmB=" +
-          ibInvalid.value;
+          '&mmB=' +
+          ibInvalid.value
       } else {
         linkActivity.value =
           activityLing.value +
-          "?mmdi=" +
+          '?mmdi=' +
           getInfoId.value +
-          "&mmF=" +
+          '&mmF=' +
           linkValue +
-          "&mmB=" +
-          ibInvalid.value;
+          '&mmB=' +
+          ibInvalid.value
       }
     } else {
-      if (activityLing.value.indexOf("?") > -1) {
+      if (activityLing.value.indexOf('?') > -1) {
         linkActivity.value =
           Host80 +
-          "/" +
+          '/' +
           activityLing.value +
-          "&mmdi=" +
+          '&mmdi=' +
           getInfoId.value +
-          "&mmF=" +
+          '&mmF=' +
           linkValue +
-          "&mmB=" +
-          ibInvalid.value;
+          '&mmB=' +
+          ibInvalid.value
       } else {
         linkActivity.value =
           Host80 +
-          "/" +
+          '/' +
           activityLing.value +
-          "?mmdi=" +
+          '?mmdi=' +
           getInfoId.value +
-          "&mmF=" +
+          '&mmF=' +
           linkValue +
-          "&mmB=" +
-          ibInvalid.value;
+          '&mmB=' +
+          ibInvalid.value
       }
     }
   }
@@ -636,7 +639,7 @@
   }
   const toIbManagement = () => {
     router.push({
-        path: '/pages/ib/customer',
+        path: '/pages/ib/subsList',
         query: { type: 2 },
       },
     )
@@ -805,7 +808,6 @@
 
   .custom-content {
     display: flex;
-    justify-content: center;
     justify-content: space-around;
     flex-wrap: wrap;
 
@@ -826,73 +828,6 @@
     }
   }
 
-
-  /* 知识库卡片 */
-  .knowledge-content {
-    padding: 8rpx 0;
-  }
-
-  .knowledge-text {
-    font-size: 28rpx;
-    line-height: 1.5;
-    text-decoration: underline;
-    text-decoration-color: rgba(255, 255, 255, 0.4);
-  }
-
-  /* 二维码弹窗样式 */
-  .qr-modal {
-    width: 560rpx;
-    background: #1e1e2a;
-    border-radius: 32rpx;
-    overflow: hidden;
-  }
-
-  .qr-header {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 24rpx 32rpx;
-    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
-  }
-
-  .qr-title {
-    font-size: 32rpx;
-    font-weight: 600;
-  }
-
-  .qr-close {
-    font-size: 48rpx;
-    color: rgba(255, 255, 255, 0.6);
-    line-height: 1;
-    cursor: pointer;
-  }
-
-  .qr-body {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    padding: 32rpx;
-  }
-
-  .qr-canvas {
-    width: 400rpx;
-    height: 400rpx;
-    background: #fff;
-    border-radius: 16rpx;
-  }
-
-  .qr-footer {
-    padding: 24rpx 32rpx;
-    border-top: 1px solid rgba(255, 255, 255, 0.1);
-    text-align: center;
-  }
-
-  .qr-tip {
-    font-size: 24rpx;
-    color: rgba(255, 255, 255, 0.6);
-    word-break: break-all;
-  }
-
   .dia-content {
     padding: 20rpx;
   }

+ 24 - 0
pages/ib/subsList.vue

@@ -0,0 +1,24 @@
+<template>
+    <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
+        <cwg-header :title="t('Home.page_ib.item2')" />
+
+        <view class="account-section">
+        </view>
+    </cwg-page-wrapper>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
+import { onLoad } from '@dcloudio/uni-app'
+import { useI18n } from 'vue-i18n' // uni-app 中已集成,但需配置
+import { customApi } from '@/service/custom'
+import { financialApi } from '@/service/financial'
+import Config from '@/config/index'
+import AddBankDialog from '@/components/AddBankDialog.vue';
+import PaymentMethodsList from './components/PaymentMethodsList.vue'
+const { t, locale } = useI18n()
+const isZh = computed(() => ['cn', 'zhHant'].includes(locale.value))
+</script>
+<style lang="scss" scoped>
+@import "@/uni.scss";
+</style>

+ 4 - 0
static/scss/global/global.scss

@@ -1343,4 +1343,8 @@ uni-content {
             }
         }
     }
+}
+
+.crm-cursor{
+  cursor: pointer;
 }

+ 236 - 197
windows/left-window.vue

@@ -1,198 +1,237 @@
 <template>
-    <view class="cwg-sidebar">
-        <view class="menu" v-for="(item, index) in menu" :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="submenuRefs" class="submenu-box" :style="{
+  <view class="cwg-sidebar">
+    <view class="menu" v-for="(item, index) in menu" :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="submenuRefs" class="submenu-box" :style="{
                 height: 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" :submenu-items="item.children" @submenu-click="handleSubmenuClick" />
-            </view>
-        </view>
+        <cwg-submenu v-if="item.children" :submenu-items="item.children" @submenu-click="handleSubmenuClick" />
+      </view>
     </view>
+  </view>
 </template>
 
 <script lang="ts" setup>
-import { ref, onMounted, nextTick, watch, onBeforeUnmount } from 'vue';
-import useRouter from "@/hooks/useRouter";
-const router = useRouter();
-import useRoute from '@/hooks/useRoute'
-const route = useRoute()
-import Config from '@/config/index'
-import { useI18n } from "vue-i18n";
-const { t, locale } = useI18n();
-interface MenuItem {
+  import { ref, onMounted, nextTick, watch, onBeforeUnmount } from 'vue'
+  import useRouter from '@/hooks/useRouter'
+
+  const router = useRouter()
+  import useRoute from '@/hooks/useRoute'
+
+  const route = useRoute()
+  import Config from '@/config/index'
+  import { useI18n } from 'vue-i18n'
+
+  const { t, locale } = useI18n()
+
+  interface MenuItem {
     key: string;
     label: string;
     icon?: string;
     children?: MenuItem[];
-}
-const emit = defineEmits(['menu-click']);
-// 菜单数据
-const menu = ref<MenuItem[]>(
+  }
+
+  const emit = defineEmits(['menu-click'])
+  // 菜单数据
+  const menu = ref<MenuItem[]>(
     [
-        {
-            isOpenMenu: false, submenuHeight: 0,
-            path: '/pages/customer/index', label: 'Shop.Index.Transaction', icon: 'crm-trade',
-            children: [
-                { path: '/pages/customer/index', label: 'Custom.Index.AccountList', icon: 'icon-client' },
-                { path: '/pages/customer/trade-history', label: 'Ib.Report.Tit1', icon: 'icon-transfer' },
-                { path: '/pages/customer/trade-position', label: 'Ib.Report.Tit4', icon: 'icon-transfer' },
-                { path: '/pages/customer/recording-history', label: 'Home.page_customer.item7', icon: 'icon-application' }
-            ]
-        },
-        {
-            isOpenMenu: false, submenuHeight: 0,
-            path: '/pages/customer/index', label: 'Latest.PaymentWallet', icon: 'crm-payment',
-            children: [
-                { path: '/pages/customer/deposit', label: 'Home.page_customer.item2', icon: 'icon-deposit' },
-                { path: '/pages/customer/withdrawal', label: 'Home.page_customer.item3', icon: 'icon-withdrawal' },
-                { path: '/pages/customer/payment-history', label: 'Home.page_customer.item4', icon: 'icon-payment' },
-                { path: '/pages/customer/transfer', label: 'Custom.Index.Transfer', icon: 'icon-transfer' }
-            ]
-        },
-        {
+      {
+        isOpenMenu: false, submenuHeight: 0,
+        path: '/pages/customer/index', label: 'Shop.Index.Transaction', icon: 'crm-trade',
+        children: [
+          { path: '/pages/customer/index', label: 'Custom.Index.AccountList', icon: 'icon-client' },
+          { path: '/pages/customer/trade-history', label: 'Ib.Report.Tit1', icon: 'icon-transfer' },
+          { path: '/pages/customer/trade-position', label: 'Ib.Report.Tit4', icon: 'icon-transfer' },
+          { path: '/pages/customer/recording-history', label: 'Home.page_customer.item7', icon: 'icon-application' },
+        ],
+      },
+      {
+        isOpenMenu: false, submenuHeight: 0,
+        path: '/pages/customer/index', label: 'Latest.PaymentWallet', icon: 'crm-payment',
+        children: [
+          { path: '/pages/customer/deposit', label: 'Home.page_customer.item2', icon: 'icon-deposit' },
+          { path: '/pages/customer/withdrawal', label: 'Home.page_customer.item3', icon: 'icon-withdrawal' },
+          { path: '/pages/customer/payment-history', label: 'Home.page_customer.item4', icon: 'icon-payment' },
+          { path: '/pages/customer/transfer', label: 'Custom.Index.Transfer', icon: 'icon-transfer' },
+        ],
+      },
+      {
+        isOpenMenu: false,
+        path: '/pages/ib/index', label: 'Home.msg.Ib', icon: 'crm-ib',
+        children: [
+          { path: '/pages/ib/index', label: 'Home.page_ib.item1', icon: 'icon-client' },
+          //  客户管理
+          { path: '/pages/ib/customer', label: 'Home.page_ib.item2', icon: 'icon-deposit' },
+          //代理管理
+          { path: '/pages/ib/subsList', label: 'Home.page_ib.item12', icon: 'icon-deposit' },
+          // 信号源列表
+          { path: '/pages/ib/agentList', label: 'Home.page_ib.item11', icon: 'icon-deposit' },
+          // 账户管理
+          { path: '/pages/ib/accountList', label: 'Home.page_ib.item10', icon: 'icon-deposit' },
+          { path: '/pages/ib/report', label: 'Home.page_ib.item3', icon: 'icon-withdrawal' },
+          { path: '/pages/ib/transfer', label: 'Home.page_ib.item4', icon: 'icon-payment' },
+          { path: '/pages/ib/withdraw', label: 'Home.page_ib.item5', icon: 'icon-transfer' },
+          { path: '/pages/ib/agent-transfer', label: 'Home.page_ib.item9', icon: 'icon-transfer' },
+          { path: '/pages/ib/recording', label: 'Home.page_ib.item7', icon: 'icon-application' },
+        ],
+      },
+      {
+        path: '/pages/analytics/analystViews', isOpenMenu: false, label: 'News.News', icon: 'crm-chart-area',
+        children: [
+          { path: '/pages/analytics/analystViews', label: 'News.Announcement', icon: 'icon-application' },
+          { path: '/pages/analytics/news', label: 'News.NewsInformation', icon: 'icon-application' },
+          {
+            path: `https://www.${Config.host}.com/${locale.value}/economic-calendar`,
+            label: 'News.FinancialCalendar',
+            icon: 'icon-application',
+            isExternal: true,
+          },
+        ],
+      },
+      {
+        path: '/pages/customer/withdrawal', isOpenMenu: false, label: 'Downloadpage.item1', icon: 'crm-download',
+        children: [],
+      },
+      {
+        path: '/pages/common/chat', isOpenMenu: false, label: 'Downloadpage.item16', icon: 'crm-headset',
+        children: [],
+        type: 'chat',
+      },
+      {
+        path: '/pages/customer/support', isOpenMenu: false, label: 'Custom.Index.Settings', icon: 'crm-sz',
+        children: [
+          {
+            path: '/pages/mine/info?type=1',
+            isOpenMenu: false,
+            label: 'PersonalManagement.Title.PersonalInformation',
+            icon: 'crm-headset',
+          },
+          {
+            path: '/pages/mine/info?type=2',
+            isOpenMenu: false,
+            label: 'PersonalManagement.Title.BankInformation',
+            icon: 'crm-headset',
+          },
+          {
+            path: '/pages/mine/info?type=3',
             isOpenMenu: false,
-            path: '/pages/ib/index', label: 'Home.msg.Ib', icon: 'crm-ib',
-            children: [
-                { path: '/pages/ib/index', label: 'Home.page_ib.item1', icon: 'icon-client' },
-                { path: '/pages/ib/customer', label: 'Home.page_ib.item2', icon: 'icon-deposit' },
-                { path: '/pages/ib/report', label: 'Home.page_ib.item3', icon: 'icon-withdrawal' },
-                { path: '/pages/ib/transfer', label: 'Home.page_ib.item4', icon: 'icon-payment' },
-                { path: '/pages/ib/withdraw', label: 'Home.page_ib.item5', icon: 'icon-transfer' },
-                { path: '/pages/ib/agent-transfer', label: 'Home.page_ib.item9', icon: 'icon-transfer' },
-                { path: '/pages/ib/recording', label: 'Home.page_ib.item7', icon: 'icon-application' }
-            ]
-        },
-        {
-            path: '/pages/analytics/analystViews', isOpenMenu: false, label: 'News.News', icon: 'crm-chart-area',
-            children: [
-                { path: '/pages/analytics/analystViews', label: 'News.Announcement', icon: 'icon-application' },
-                { path: '/pages/analytics/news', label: 'News.NewsInformation', icon: 'icon-application' },
-                { path: `https://www.${Config.host}.com/${locale.value}/economic-calendar`, label: 'News.FinancialCalendar', icon: 'icon-application', isExternal: true },
-            ]
-        },
-        {
-            path: '/pages/customer/withdrawal', isOpenMenu: false, label: 'Downloadpage.item1', icon: 'crm-download',
-            children: []
-        },
-        {
-            path: '/pages/common/chat', isOpenMenu: false, label: 'Downloadpage.item16', icon: 'crm-headset',
-            children: [],
-            type: 'chat'
-        },
-        {
-            path: '/pages/customer/support', isOpenMenu: false, label: 'Custom.Index.Settings', icon: 'crm-sz',
-            children: [
-                { path: '/pages/mine/info?type=1', isOpenMenu: false, label: 'PersonalManagement.Title.PersonalInformation', icon: 'crm-headset' },
-                { path: '/pages/mine/info?type=2', isOpenMenu: false, label: 'PersonalManagement.Title.BankInformation', icon: 'crm-headset' },
-                { path: '/pages/mine/info?type=3', isOpenMenu: false, label: 'PersonalManagement.Title.FileManagement', icon: 'crm-headset' },
-                { path: '/pages/mine/info?type=4', isOpenMenu: false, label: 'PersonalManagement.Title.SecurityCenter', icon: 'crm-headset' },
-                { path: '/pages/common/notice', isOpenMenu: false, label: 'News.Notice', icon: 'crm-headset' },
-            ]
-        },
-    ]);
-const submenuRefs = ref<any[]>([]);
-const measureHeight = (element: HTMLElement): number => {
-    const originalDisplay = element.style.display;
-    const originalPosition = element.style.position;
-    const originalVisibility = element.style.visibility;
-    const originalWidth = element.style.width;
-    element.style.display = 'block';
-    element.style.position = 'absolute';
-    element.style.visibility = 'hidden';
-    element.style.width = '100%';
-    const height = element.scrollHeight || element.offsetHeight;
-    element.style.display = originalDisplay;
-    element.style.position = originalPosition;
-    element.style.visibility = originalVisibility;
-    element.style.width = originalWidth;
+            label: 'PersonalManagement.Title.FileManagement',
+            icon: 'crm-headset',
+          },
+          {
+            path: '/pages/mine/info?type=4',
+            isOpenMenu: false,
+            label: 'PersonalManagement.Title.SecurityCenter',
+            icon: 'crm-headset',
+          },
+          { path: '/pages/common/notice', isOpenMenu: false, label: 'News.Notice', icon: 'crm-headset' },
+        ],
+      },
+    ])
+  const submenuRefs = ref<any[]>([])
+  const measureHeight = (element: HTMLElement): number => {
+    const originalDisplay = element.style.display
+    const originalPosition = element.style.position
+    const originalVisibility = element.style.visibility
+    const originalWidth = element.style.width
+    element.style.display = 'block'
+    element.style.position = 'absolute'
+    element.style.visibility = 'hidden'
+    element.style.width = '100%'
+    const height = element.scrollHeight || element.offsetHeight
+    element.style.display = originalDisplay
+    element.style.position = originalPosition
+    element.style.visibility = originalVisibility
+    element.style.width = originalWidth
 
-    return height;
-};
-const updateSubmenuHeight = (index: number) => {
-    const refs = submenuRefs.value;
+    return height
+  }
+  const updateSubmenuHeight = (index: number) => {
+    const refs = submenuRefs.value
     if (refs && refs[index]) {
-        const el = refs[index].$el || refs[index];
-        const height = measureHeight(el);
-        if (height > 0) {
-            menu.value[index].submenuHeight = height;
-        }
+      const el = refs[index].$el || refs[index]
+      const height = measureHeight(el)
+      if (height > 0) {
+        menu.value[index].submenuHeight = height
+      }
     }
-};
-function handleClick(index: number) {
+  }
+
+  function handleClick(index: number) {
     if (!menu.value[index].children || menu.value[index].children.length == 0) {
-        // #ifdef H5
-        if (menu.value[index].type === 'chat') {
-            if (window.LiveChatWidget) {
-                window.LiveChatWidget.call("maximize");
-            }
-        } else {
-            router.push(menu.value[index].path);
+      // #ifdef H5
+      if (menu.value[index].type === 'chat') {
+        if (window.LiveChatWidget) {
+          window.LiveChatWidget.call('maximize')
         }
-        // #endif
+      } else {
+        router.push(menu.value[index].path)
+      }
+      // #endif
 
-        // #ifdef APP-VUE
-        router.push(menu.value[index].path);
-        // #endif
-        return;
+      // #ifdef APP-VUE
+      router.push(menu.value[index].path)
+      // #endif
+      return
     }
-    menu.value[index].isOpenMenu = !menu.value[index].isOpenMenu;
-}
-watch(route, () => {
-    const currentPath = route.path;
+    menu.value[index].isOpenMenu = !menu.value[index].isOpenMenu
+  }
+
+  watch(route, () => {
+    const currentPath = route.path
     menu.value.forEach((item, index) => {
-        if (item.children) {
-            const isActive = item.children.some(child => child.path.includes(currentPath));
-            menu.value[index].isOpenMenu = isActive;
-            if (isActive) {
-                nextTick(() => {
-                    updateSubmenuHeight(index);
-                });
-            }
+      if (item.children) {
+        const isActive = item.children.some(child => child.path.includes(currentPath))
+        menu.value[index].isOpenMenu = isActive
+        if (isActive) {
+          nextTick(() => {
+            updateSubmenuHeight(index)
+          })
         }
-    });
-}, { immediate: true })
+      }
+    })
+  }, { immediate: true })
 
-// 添加窗口大小变化监听
-const handleResize = () => {
+  // 添加窗口大小变化监听
+  const handleResize = () => {
     menu.value.forEach((item, index) => {
-        if (item.children) {
-            updateSubmenuHeight(index);
-        }
-    });
-};
+      if (item.children) {
+        updateSubmenuHeight(index)
+      }
+    })
+  }
 
-onMounted(() => {
+  onMounted(() => {
     nextTick(() => {
-        menu.value.forEach((item, index) => {
-            if (item.children) {
-                updateSubmenuHeight(index);
-            }
-        });
-    });
+      menu.value.forEach((item, index) => {
+        if (item.children) {
+          updateSubmenuHeight(index)
+        }
+      })
+    })
 
     // 添加窗口resize监听
-    window.addEventListener('resize', handleResize);
-});
+    window.addEventListener('resize', handleResize)
+  })
 
-// 组件卸载时移除监听
-onBeforeUnmount(() => {
-    window.removeEventListener('resize', handleResize);
-});
+  // 组件卸载时移除监听
+  onBeforeUnmount(() => {
+    window.removeEventListener('resize', handleResize)
+  })
 </script>
 
 <style scoped lang="scss">
-@import "@/uni.scss";
+  @import "@/uni.scss";
 
-.cwg-sidebar {
+  .cwg-sidebar {
     width: 100%;
     color: #6c8595;
     height: calc(100vh - 56px);
@@ -207,65 +246,65 @@ onBeforeUnmount(() => {
     border-right: 1px solid rgba(108, 133, 149, 0.12);
 
     .logo {
-        width: px2rpx(54);
+      width: px2rpx(54);
     }
 
     .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;
+      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;
-        }
+      .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);
+      }
     }
-}
+  }
 
-@media screen and (max-width: 1100px) {
+  @media screen and (max-width: 1100px) {
 
     .cwg-sidebar:not(:hover) .menu-label,
     .cwg-sidebar:not(:hover) .submenu-box,
     .cwg-sidebar:not(:hover) .chevron-icon {
-        display: none;
+      display: none;
     }
 
     .cwg-sidebar:hover .menu-label,
     .cwg-sidebar:hover .submenu-box,
     .cwg-sidebar:hover .chevron-icon {
-        display: block;
+      display: block;
     }
-}
+  }
 </style>