فهرست منبع

feature: 币种配置页面

ljc 6 ماه پیش
والد
کامیت
c53f446e36

+ 16 - 105
index.html

@@ -4,115 +4,26 @@
     <meta charset="UTF-8" />
     <link rel="icon" href="/favicon.ico" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>Vue admin perfect</title>
+    <link href="/css/preloading.css" rel="stylesheet" type="text/css">
+    <title>CWG-管理系统</title>
   </head>
   <body>
+  <noscript>
+    <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
+      Please enable it to continue.</strong>
+  </noscript>
     <div id="app">
-      <style>
-        .first-loading-wrp {
-          display: flex;
-          flex-direction: column;
-          align-items: center;
-          justify-content: center;
-          height: 90vh;
-          min-height: 90vh;
-        }
-        .first-loading-wrp > h1 {
-          font-size: 30px;
-          font-weight: bolder;
-        }
-        .first-loading-wrp .loading-wrp {
-          display: flex;
-          align-items: center;
-          justify-content: center;
-          padding: 98px;
-        }
-        .dot {
-          position: relative;
-          box-sizing: border-box;
-          display: inline-block;
-          width: 64px;
-          height: 64px;
-          font-size: 64px;
-          transform: rotate(45deg);
-          animation: antRotate 1.2s infinite linear;
-        }
-
-        .dot i {
-          position: absolute;
-          display: block;
-          width: 28px;
-          height: 28px;
-          background-color: #1890ff;
-          border-radius: 100%;
-          opacity: 0.3;
-          transform: scale(0.75);
-          transform-origin: 50% 50%;
-          animation: antSpinMove 1s infinite linear alternate;
-        }
-
-        .dot i:nth-child(1) {
-          top: 0;
-          left: 0;
-        }
-
-        .dot i:nth-child(2) {
-          top: 0;
-          right: 0;
-          -webkit-animation-delay: 0.4s;
-          animation-delay: 0.4s;
-        }
-
-        .dot i:nth-child(3) {
-          right: 0;
-          bottom: 0;
-          -webkit-animation-delay: 0.8s;
-          animation-delay: 0.8s;
-        }
-
-        .dot i:nth-child(4) {
-          bottom: 0;
-          left: 0;
-          -webkit-animation-delay: 1.2s;
-          animation-delay: 1.2s;
-        }
-        @keyframes antRotate {
-          to {
-            -webkit-transform: rotate(405deg);
-            transform: rotate(405deg);
-          }
-        }
-
-        @-webkit-keyframes antRotate {
-          to {
-            -webkit-transform: rotate(405deg);
-            transform: rotate(405deg);
-          }
-        }
-
-        @keyframes antSpinMove {
-          to {
-            opacity: 1;
-          }
-        }
-
-        @-webkit-keyframes antSpinMove {
-          to {
-            opacity: 1;
-          }
-        }
-      </style>
-      <div id="vue-admin-perfect">
-        <div class="first-loading-wrp">
-          <div class="loading-wrp">
-            <span class="dot dot-spin">
-              <i></i>
-              <i></i>
-              <i></i>
-              <i></i>
-            </span>
+      <div id="pre" style="position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 100000; ">
+        <div style="background-color: rgba(0, 0, 0, .8); width: 100%; height: 100%;">
+          <div style="position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);">
+            <div class="line-scale">
+              <div></div>
+              <div></div>
+              <div></div>
+              <div></div>
+              <div></div>
+            </div>
           </div>
-          <h1>vue-admin-perfect</h1>
         </div>
       </div>
     </div>

+ 1 - 0
package.json

@@ -35,6 +35,7 @@
     "fuse.js": "^6.6.2",
     "jszip": "^3.9.1",
     "lodash": "^4.17.21",
+    "lodash-es": "^4.17.21",
     "mavon-editor": "^2.10.4",
     "md-editor-v3": "^1.11.3",
     "nprogress": "^0.2.0",

+ 71 - 0
src/components/pagePagination/index.vue

@@ -0,0 +1,71 @@
+<template>
+  <div v-if="pageInfo.rowTotal" class="crm_pagination">
+    <div class="crm_page_total">
+      <span>
+        {{ $t('Page.total.item1') }}
+      </span>
+      <span>{{ pageInfo.rowTotal }}</span>
+      <span>
+        {{ $t('Page.total.item2') }}
+      </span>
+    </div>
+    <el-pagination
+      class="page"
+      background
+      layout="sizes, prev, pager, next"
+      :page-sizes="[10, 20, 50, 100]"
+      :page-size="pageInfo.row"
+      :total="pageInfo.rowTotal"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange"
+    />
+  </div>
+</template>
+
+<script setup name="pagePagination">
+  import { defineProps, defineEmits } from 'vue'
+
+  const props = defineProps({
+    // 数据信息
+    pagerInfo: {
+      type: Object,
+      required: true,
+    },
+  })
+
+  const emit = defineEmits(['current-change', 'size-change'])
+
+  const pageInfo = props.pagerInfo
+
+  const handleCurrentChange = (val) => {
+    emit('current-change', val)
+  }
+
+  const handleSizeChange = (val) => {
+    emit('size-change', val)
+  }
+</script>
+
+<style scoped lang="scss">
+  .crm_pagination {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    margin-top: 16px;
+
+    .crm_page_total {
+      color: #606266;
+      font-size: 14px;
+
+      span {
+        margin: 0 4px;
+      }
+    }
+
+    .page {
+      :deep(.el-pagination__total) {
+        display: none;
+      }
+    }
+  }
+</style>

+ 10 - 3
src/config/index.ts

@@ -13,29 +13,36 @@ export const PRIMARY_COLOR = '#409eff'
 const ht = window.location.protocol
 const ho = window.location.host.split('.')[1]
 const c = import.meta.env.VITE_APP_ENV
-let Host00, Host85
+let Host00, Host85, Host05
 switch (c) {
   // 测试环境
   case 'test':
     Host00 = ht + '//secure.' + ho + '.com'
     Host85 = ht + '//ad.' + ho + '.com'
+    Host05 = ht + '//file.' + ho + '.com'
+    // Host05 = "http://103.171.34.61:8705"
     break
   // 生产环境
   case 'production':
     Host00 = ht + '//secure.' + ho + '.com'
     Host85 = ht + '//ad.' + ho + '.com'
+    Host05 = ht + '//file.' + ho + '.com'
     break
 
   default:
     // 开发环境
     Host00 = 'http://103.214.175.29:8000'
-    Host85 = 'http://103.171.34.61:8500'
-    Host85 = 'http://192.168.0.18:8500'
+    Host85 = 'https://ad.44a5c8109e4.com'
+    // Host85="http://103.171.34.61:8500" // 测试
+    Host05 = 'http://103.171.34.61:8705'
+    Host85 = 'http://192.168.0.18:8500' // 高超本地
+    // Host85="http://192.168.0.30:8500"
     break
 }
 const config = {
   Host00,
   Host85,
+  Host05,
   Host80: ht + '//secure.' + ho + '.com',
   Code: {
     StatusOK: 200,

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2356 - 18
src/i18n/locales/cn.json


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2265 - 13
src/i18n/locales/en.json


+ 26 - 17
src/routers/modules/uCard.ts

@@ -81,23 +81,32 @@ const uCardRoute = {
       name: 'R-Business',
       component: () => import('@/views/card/CardBusiness/index.vue'),
     },
-    //   {
-    //     meta: {
-    //       OnBreadCrumb: true,
-    //     },
-    //     path: 'virtual',
-    //     name: 'R-VirtualCard',
-    //     component: () => import(/* webpackChunkName: "VirtualCard" */ '@/views/card/index.vue'),
-    //   },
-    //   {
-    //     meta: {
-    //       OnBreadCrumb: true,
-    //     },
-    //     path: 'transactions',
-    //     name: 'R-Transactions',
-    //     component: () =>
-    //       import(/* webpackChunkName: "Transactions" */ '@/views/card/Transactions.vue'),
-    //   },
+    {
+      meta: {
+        OnBreadCrumb: true,
+      },
+      path: 'virtual',
+      name: 'R-VirtualCard',
+      component: () => import('@/views/card/VirtualCard/index.vue'),
+    },
+    {
+      meta: {
+        OnBreadCrumb: true,
+      },
+      path: 'transactions',
+      name: 'R-Transactions',
+      component: () =>
+        import(/* webpackChunkName: "Transactions" */ '@/views/card/CardTransactions/index.vue'),
+    },
+    {
+      meta: {
+        OnBreadCrumb: true,
+      },
+      path: 'global/currency',
+      name: 'R-GlobalCurrency',
+      component: () =>
+        import(/* webpackChunkName: "GlobalCurrency" */ '@/views/card/GlobalCurrency/index.vue'),
+    },
     //   {
     //     meta: {
     //       OnBreadCrumb: true,

+ 50 - 0
src/service/ucard.ts

@@ -153,6 +153,56 @@ class UCardService extends Service {
   async kycList(params = {}) {
     return await this.post('/wasabi/merchant/kyc/page', params)
   }
+  // 查询卡片余额
+  async ucardBalance(params = {}) {
+    return await this.post('/wasabi/card/balance', params)
+  }
+
+  // 更新银行卡信息
+  async getCardInfo(params = {}) {
+    return await this.post('/wasabi/card/info/refresh', params)
+  }
+
+  // 查看cvv
+  async queryCvv(params = {}) {
+    return await this.post('/wasabi/card/query/cvv', params)
+  }
+  // 重新充值默认金额
+  async rechargeUpdate(params = {}) {
+    return await this.post('/wasabi/recharge/default/amount', params)
+  }
+  // 获取卡片列表
+  async cardList(params = {}) {
+    return await this.post('/wasabi/card/page', params)
+  }
+  // 查询交易记录分页列表
+  async transactionsList(params = {}) {
+    return await this.post('/wasabi/card/transac/page', params)
+  }
+  //查询最新汇率
+  async globalLatestExchangeRate(params = {}) {
+    return await this.post('/wasabi/global/query/latest/exchange/rate', params)
+  }
+  //更新手续费率
+  async updateGlobalFee(params = {}) {
+    return await this.post('/wasabi/global/currencies/config', params)
+  }
+  //币种更新
+  async globalCurrenciesSave(params = {}) {
+    return await this.post('/wasabi/global/currencies/save', params)
+  }
+  //币种下拉列表
+  async globalCurrenciesDropdown(params = {}) {
+    return await this.post('/wasabi/global/currencies/dropdown', params)
+  }
+  //查询币种全局配置
+  async globalCurrenciesConfig(params = {}) {
+    return await this.post('/wasabi/global/query/currencies/config', params)
+  }
+  //币种列表
+  async globalCurrenciesList(params = {}) {
+    return await this.post('/wasabi/global/currencies/list', params)
+  }
 }
 
 export default new UCardService()

+ 21 - 0
src/styles/cwg_common.scss

@@ -1034,4 +1034,25 @@ a {
 .el-input__inner {
   width: 100%;
 }
+.el-input-number{
+  width: 100%;
+  .el-input__inner{
+    text-align: left!important;
+    &::placeholder {
+      text-align: left;
+    }
+  }
+}
+input[type="number"]::-webkit-outer-spin-button,
+input[type="number"]::-webkit-inner-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
 
+/* Firefox */
+input[type="number"] {
+  -moz-appearance: textfield;
+}
+.div-hidden{
+  display: none !important;
+}

+ 3 - 0
src/utils/const.ts

@@ -0,0 +1,3 @@
+// 通用常量文件
+// 10位小数正则
+export const floatReg = /^\d+(\.\d{0,10})?$/

+ 24 - 0
src/views/card/CardTransactions/index.scss

@@ -0,0 +1,24 @@
+#review_Email {
+  .crm_search {
+    .search_action_btn {
+      .delete {
+        background-color: #a1a1a1;
+      }
+
+      .delete.active {
+        background-color: #368fec;
+      }
+    }
+  }
+
+  .el-table .state {
+    display: inline-block;
+    min-width: 80px;
+    max-width: 150px;
+    box-sizing: border-box;
+    line-height: 1.5;
+    border-radius: 2px;
+    padding: 2px 10px;
+    color: #ffffff;
+  }
+}

+ 479 - 0
src/views/card/CardTransactions/index.vue

@@ -0,0 +1,479 @@
+<template>
+  <div
+    id="review_Email"
+    v-loading="pictLoading"
+    class="view"
+    :element-loading-background="loadingBackground"
+  >
+    <div class="crm_search">
+      <el-form ref="formRef" label-position="" :model="search" label-width="">
+        <el-row>
+          <el-col :span="24" :md="24" :lg="24">
+            <el-form-item>
+              <el-select
+                v-model="search.tag"
+                class="crm_search_down crm-border-radius-no"
+                :placeholder="$t('Placeholder.Choose')"
+              >
+                <el-option :label="$t('Label.CidAccount')" :value="1"></el-option>
+                <el-option :label="$t('Ucard.KycAuth.item2')" :value="2"></el-option>
+                <el-option :label="$t('Ucard.KycAuth.item3')" :value="3"></el-option>
+                <el-option :label="$t('Ucard.Transactions.s2')" :value="5"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-input
+                v-if="search.tag == 1"
+                v-model.trim="search.cId"
+                class="crm-border-left-no crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+              <el-input
+                v-if="search.tag == 2"
+                v-model.trim="search.mobile"
+                class="crm-border-left-no crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+              <el-input
+                v-if="search.tag == 3"
+                v-model.trim="search.email"
+                class="crm-border-left-no crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+
+              <el-input
+                v-if="search.tag == 5"
+                v-model.trim="search.currency"
+                class="crm-border-left-no crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+              <el-select
+                v-if="search.tag == 7"
+                v-model="search.dataType"
+                class="crm-border-left-no crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Choose')"
+                @change="toSearch"
+              >
+                <el-option value="A" :label="$t('Ucard.Transactions.t1')"></el-option>
+                <el-option value="S" :label="$t('Ucard.Transactions.t17')"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-input
+                v-model.trim="search.cardNumber"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Input') + $t('Ucard.Transactions.s1')"
+                @keyup.enter="toSearch"
+              ></el-input>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-select
+                v-model="search.type"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('Ucard.Transactions.s3')"
+                @change="toSearch"
+              >
+                <el-option value="auth" :label="$t('Ucard.Transactions.t1')"></el-option>
+                <el-option value="refund" :label="$t('Ucard.Transactions.t2')"></el-option>
+                <el-option value="verification" :label="$t('Ucard.Transactions.t3')"></el-option>
+                <el-option value="Void" :label="$t('Ucard.Transactions.t4')"></el-option>
+                <el-option value="withdraw" :label="$t('R-VirtualCard-Btn11')"></el-option>
+                <el-option value="maintain_fee" :label="$t('Ucard.Transactions.t5')"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-select
+                v-model="search.status"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('Ucard.Transactions.s5')"
+                @change="toSearch"
+              >
+                <el-option value="success" :label="$t('Ucard.Transactions.t18')"></el-option>
+                <el-option value="fail" :label="$t('Ucard.Transactions.t25')"></el-option>
+                <el-option value="wait_process" :label="$t('card.Status.t5')"></el-option>
+                <el-option value="processing" :label="$t('card.Status.t3')"></el-option>
+                <el-option value="authorized" :label="$t('Ucard.Transactions.t24')"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button class="crm-border-radius-no crm-border-left-no" @click="toSearch">
+                <el-icon><Search /></el-icon>
+              </el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item>
+          <el-button
+            v-if="display['R-Transactions-Export'] && display['R-Transactions-Export'].show"
+            type="primary"
+            style="margin-left: 8px"
+            @click="exportAgents"
+            >{{ $t('Btn.Export') }}</el-button
+          >
+        </el-form-item>
+      </el-form>
+
+      <div class="card-mock-demo" style="margin: 30px 0">
+        <el-table :data="mock_tableData" stripe style="margin-top: 20px; width: 100%">
+          <el-table-column prop="" align="left" :label="$t('Label.CidAccount')">
+            <template #default="scope">
+              <span
+                v-if="scope.row.cId && display['R-Transactions-Btn1'].show"
+                class="crm-text-underline"
+                @click="accountOpen(scope.row.cId)"
+                >{{ scope.row.cId || '--' }}</span
+              >
+              <span v-else>{{ scope.row.cId || '--' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="" align="left" :label="$t('Label.Name')">
+            <template #default="scope">
+              <span v-if="scope.row.firstName">{{ scope.row.firstName + ' ' }}</span>
+              <span v-if="scope.row.middle">{{ scope.row.middle + ' ' }}</span>
+              <span v-if="scope.row.lastName">{{ scope.row.lastName }}</span>
+              <span v-if="!scope.row.firstName && !scope.row.lastName && !scope.row.middle"
+                >--</span
+              >
+            </template>
+          </el-table-column>
+          <el-table-column prop="" align="left" :label="$t('Label.Email')">
+            <template #default="scope">
+              {{ scope.row.email || '--' }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="mobile" align="left" :label="$t('Ucard.KycAuth.item2')">
+            <template #default="scope"> {{ scope.row.areaCode }} {{ scope.row.mobile }} </template>
+          </el-table-column>
+          <el-table-column prop="cardNumber" align="left" :label="$t('Ucard.Transactions.item2')">
+            <template #default="scope">
+              <span
+                v-if="scope.row.cardNumber"
+                class="crm-text-underline"
+                @click="cardOpen(scope.row.cardNumber)"
+                >{{ scope.row.cardNumber || '--' }}</span
+              >
+              <span v-else>{{ scope.row.cardNumber || '--' }}</span>
+            </template>
+          </el-table-column>
+          <!-- <el-table-column prop="cardTypeId" align="left" :label="$t('Ucard.Transactions.item1')" /> -->
+          <el-table-column prop="currency" align="left" :label="$t('Ucard.Transactions.item3')" />
+          <el-table-column prop="amount" align="left" :label="$t('Ucard.Transactions.item4')" />
+          <el-table-column prop="fee" align="left" :label="$t('Ucard.Transactions.item5')" />
+          <el-table-column prop="tradeNo" align="left" :label="$t('Ucard.Transactions.item8')" />
+          <el-table-column prop="type" align="left" :label="$t('Ucard.Transactions.item9')">
+            <template #default="scope">
+              <span v-if="scope.row.type === 'auth'">{{ $t('Ucard.Transactions.t1') }}</span>
+              <span v-else-if="scope.row.type === 'refund'">{{ $t('Ucard.Transactions.t2') }}</span>
+              <span v-else-if="scope.row.type === 'verification'">{{
+                $t('Ucard.Transactions.t3')
+              }}</span>
+              <!--              <span v-else-if="scope.row.type === 'auth'">{{ $t('Ucard.Transactions.t4') }}</span>-->
+              <span v-else-if="scope.row.type === 'withdraw'">{{ $t('R-VirtualCard-Btn11') }}</span>
+              <span v-else-if="scope.row.type === 'maintain_fee'">{{
+                $t('Ucard.Transactions.t5')
+              }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="status" align="left" :label="$t('Ucard.Transactions.item11')">
+            <template #default="scope">
+              <span
+                v-if="scope.row.status === 'succeed' || scope.row.status === 'success'"
+                class="state crm_state_blue"
+                >{{ $t('Ucard.Transactions.t18') }}</span
+              >
+              <span
+                v-else-if="scope.row.status === 'failed' || scope.row.status === 'fail'"
+                class="state crm_state_gray"
+                >{{ $t('Ucard.Transactions.t25') }}</span
+              >
+              <span v-else-if="scope.row.status === 'processing'" class="state crm_state_orange">{{
+                $t('card.Status.t3')
+              }}</span>
+              <span
+                v-else-if="scope.row.status === 'wait_process'"
+                class="state crm_state_yellow"
+                >{{ $t('card.Status.t5') }}</span
+              >
+              <span v-else class="state crm_state_green">{{ $t('Ucard.Transactions.t24') }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="" align="center" :label="$t('Label.Action')">
+            <template #default="scope">
+              <el-dropdown trigger="click" @command="handleCommand">
+                <span class="el-dropdown-link crm-cursor">
+                  <i style="font-weight: bold; font-size: 20px" class="iconfont iconcaidan"></i>
+                </span>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item
+                      v-if="display['R-Transactions-Btn1'].show"
+                      :command="{ type: 1, row: scope.row }"
+                    >
+                      <el-icon><Operation /></el-icon>
+                      <span>{{ $t('R-Hire-Check') }}</span>
+                    </el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+    <div v-if="pagerInfo.rowTotal" class="crm_pagination">
+      <div class="crm_page_total">
+        <span>{{ $t('Page.total.item1') }}</span>
+        <span>{{ pagerInfo.rowTotal }}</span>
+        <span>{{ $t('Page.total.item2') }}</span>
+      </div>
+      <el-pagination
+        class="page"
+        background
+        layout="sizes, prev, pager, next"
+        :page-sizes="[10, 20, 50, 100]"
+        :page-size="pagerInfo.row"
+        :total="pagerInfo.rowTotal"
+        @current-change="handleCurrentChange"
+        @size-change="handleSizeChange"
+      >
+      </el-pagination>
+    </div>
+    <detailed-info-cid
+      :dialog-info-cid="dialogInfoCid"
+      :form-info="formInfo"
+      :is-trading="true"
+      @close="close"
+    >
+    </detailed-info-cid>
+    <div v-if="dialogInfoCid" class="crm_verified_info_mask" @click="closeDia"></div>
+    <ViewCardSingle
+      :dialog-info-trading-single="dialogInfoTradingSingle"
+      :form-list="formSingle"
+      :editor-type="editorType"
+      @close-single="closeSingle"
+    >
+    </ViewCardSingle>
+    <div v-if="dialogInfoTradingSingle" class="crm_verified_info_mask" @click="closeSingle"></div>
+  </div>
+</template>
+
+<script setup>
+  import { ref, reactive, computed, onMounted, watch, inject } from 'vue'
+  import { useRouter } from 'vue-router'
+  import Service from '@/service/ucard'
+  import Config from '@/config/index'
+  import Service1 from '@/service/customer'
+  import { Operation, Search } from '@element-plus/icons-vue'
+  import { useI18n } from 'vue-i18n'
+  import DetailedInfoCid from '@/views/components/DetailedInfoCid'
+  import ViewCardSingle from '@/views/components/ViewCardSingle'
+  import { exportExcel } from '@/utils/export'
+
+  const { t } = useI18n()
+  const router = useRouter()
+  const Session = inject('session')
+  const pigeon = inject('pigeon')
+  const { Code } = Config
+
+  // 响应式数据
+  const pictLoading = ref(false)
+  const dialogInfoTradingSingle = ref(false)
+  const formSingle = ref({})
+  const editorType = ref(4)
+  const formRef = ref(null)
+
+  const search = reactive({
+    tag: 1,
+    mobile: '',
+    cId: '',
+    email: '',
+    cardNumber: '',
+    currency: '',
+    type: '',
+    dataType: '',
+    status: '',
+  })
+
+  const mock_tableData = ref([])
+  const pagerInfo = reactive({ row: 10, current: 1, pageTotal: 0, rowTotal: 0 })
+  const dialogInfoCid = ref(false)
+  const formInfo = ref({})
+
+  const loadingBackground = 'rgba(43, 48, 67, 0.65)'
+
+  // 计算属性
+  const display = computed(() => {
+    return JSON.parse(Session.Get('display', true))
+  })
+
+  // 方法
+  const exportAgents = async () => {
+    exportExcel(
+      pigeon,
+      '/wasabi/card/transac/record/export',
+      { ...search },
+      'Bank_Card_Transactions'
+    )
+  }
+  //银行卡详情
+  const cardOpen = (cardNumber) => {
+    router.push({ name: 'R-CardDetail', params: { cardNumber: cardNumber } })
+  }
+  //详细信息cid 大概不要了
+  const accountOpen = (cId) => {
+    router.push({ name: 'R-CustomerDetail', params: { cId: cId } })
+  }
+
+  const searchSingleCid = async (cId) => {
+    let res = await Service1.cidRealSingle({
+      id: cId,
+    })
+    if (res.code == Code.StatusOK) {
+      if (editor.value) {
+        formList.value = res.data
+        dialogInfoAdd.value = true
+      } else {
+        formInfo.value = res.data
+        searchRealAll(cId)
+      }
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+  }
+
+  const searchRealAll = async (cId) => {
+    let res = await Service1.realCustomerListAll({
+      cId: cId,
+      page: {
+        current: 1,
+        row: 1,
+      },
+    })
+    if (res.code == Code.StatusOK) {
+      formInfo.value.realList = res.data
+      dialogInfoCid.value = true
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+  }
+
+  const closeDia = () => {
+    dialogInfoCid.value = false
+  }
+
+  const close = (val) => {
+    dialogInfoCid.value = val
+  }
+
+  const toSearch = () => {
+    pagerInfo.current = 1
+    searchFunc()
+  }
+
+  const updateCardTypes = async () => {
+    let res = await Service.updateCardTypes({})
+    if (res.code == Code.StatusOK) {
+      pigeon.MessageOK(t('Msg.SearchSuccess'))
+      searchFunc()
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+  }
+
+  const closeSingle = () => {
+    dialogInfoTradingSingle.value = false
+  }
+
+  const handleCommand = (command) => {
+    switch (command.type) {
+      case 1:
+        dialogInfoTradingSingle.value = true
+        formSingle.value = command.row
+        break
+    }
+  }
+
+  const searchFunc = async () => {
+    pictLoading.value = true
+    if (!display.value['R-Transactions-Search'].show) {
+      pigeon.MessageWarning(t('Msg.NotDisplay'))
+      pictLoading.value = false
+      return
+    }
+    let res = await Service.transactionsList({
+      ...search,
+      page: {
+        current: pagerInfo.current,
+        row: pagerInfo.row,
+      },
+    })
+    if (res.code == Code.StatusOK) {
+      mock_tableData.value = res.data
+      if (res.page != null) {
+        pagerInfo.rowTotal = res.page.rowTotal
+        pagerInfo.pageTotal = res.page.pageTotal
+      } else {
+        pagerInfo.rowTotal = 0
+      }
+      pigeon.MessageOK(t('Msg.SearchSuccess'))
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+    pictLoading.value = false
+  }
+
+  const handleSizeChange = (val) => {
+    pagerInfo.row = val
+    searchFunc()
+  }
+
+  const handleCurrentChange = (val) => {
+    pagerInfo.current = val
+    searchFunc()
+  }
+
+  // 监听器
+  watch(
+    () => search.tag,
+    () => {
+      search.mobile = ''
+      search.email = ''
+      search.cId = ''
+      search.cardNumber = ''
+      search.currency = ''
+      search.type = ''
+      search.dataType = ''
+      search.status = ''
+    }
+  )
+
+  // 生命周期
+  onMounted(() => {
+    searchFunc()
+  })
+</script>
+
+<style scoped lang="scss">
+  @import 'index.scss';
+</style>
+<style lang="scss">
+  #review_Email {
+    .dialog_header_w {
+      .crm_search_down {
+        width: 400px;
+      }
+    }
+  }
+</style>

+ 12 - 0
src/views/card/GlobalCurrency/const.ts

@@ -0,0 +1,12 @@
+// 常量文件
+export const transferType = {
+  1: 'B2B',
+  2: 'B2C',
+  3: 'C2C',
+  4: 'C2B',
+}
+
+export const payoutMethod = {
+  1: 'global.p13',
+  7: 'global.p14',
+}

+ 729 - 0
src/views/card/GlobalCurrency/index.vue

@@ -0,0 +1,729 @@
+<template>
+  <div
+    id="review_Email"
+    v-loading="pictLoading"
+    class="view"
+    element-loading-background="rgba(43, 48, 67, 0.65)"
+    element-loading-spinner="el-icon-loading"
+  >
+    <div class="crm_search">
+      <el-form ref="searchForm" label-position="" :model="search" label-width="">
+        <el-row>
+          <el-col :span="24" :md="24" :lg="24">
+            <el-form-item style="margin-right: 10px">
+              <el-select
+                v-model="search.payoutCurrency"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('global.p1')"
+                @change="toSearch"
+              >
+                <el-option
+                  v-for="(item, index) in currenciesDropdown"
+                  :key="index"
+                  :label="item"
+                  :value="item"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-select
+                v-model="search.transferTypeId"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('global.p2')"
+                @change="toSearch"
+              >
+                <el-option
+                  v-for="([key, value], index) in Object.entries(transferType)"
+                  :key="index"
+                  :label="value"
+                  :value="key"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-select
+                v-model="search.payoutMethodId"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('global.p3')"
+                @change="toSearch"
+              >
+                <el-option
+                  v-for="([key, value], index) in Object.entries(payoutMethod)"
+                  :key="index"
+                  :label="$t(value)"
+                  :value="key"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-select
+                v-model="search.status"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('Ucard.CardType.s3')"
+                @change="toSearch"
+              >
+                <el-option :label="$t('Ucard.CardType.t5')" value="offline"></el-option>
+                <el-option :label="$t('Ucard.CardType.t6')" value="online"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button
+                class="crm-border-radius-no crm-border-left-no"
+                icon="el-icon-search"
+                @click="toSearch"
+              ></el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item>
+          <div class="search_action_btn">
+            <el-button
+              v-if="display['R-GlobalCurrency-Sync'].show"
+              type="primary"
+              :loading="syncLoading"
+              class="crm-cursor"
+              @click="updateCardTypes"
+            >
+              {{ $t('R-GlobalCurrency-Sync') }}
+            </el-button>
+          </div>
+        </el-form-item>
+      </el-form>
+
+      <div class="card-mock-demo" style="margin-top: 30px">
+        <el-table :data="mergeFirstRowData" stripe style="margin-top: 20px; width: 100%">
+          <el-table-column prop="feeRateConfig" align="left" :label="$t('global.p5')" />
+          <el-table-column prop="fixedFeeConfig" align="left" :label="$t('global.p6')" />
+          <el-table-column :label="$t('Ucard.Business.item20')" align="center">
+            <template #default="scope">
+              <el-dropdown trigger="click" @command="handleCommand">
+                <span class="el-dropdown-link crm-cursor">
+                  <i style="font-weight: bold; font-size: 20px" class="iconfont iconcaidan"></i>
+                </span>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item
+                      v-if="
+                        display['R-GlobalCurrency-Update'] &&
+                        display['R-GlobalCurrency-Update'].show
+                      "
+                      :command="{ type: 1, row: scope.row }"
+                    >
+                      <el-icon><Edit /></el-icon>
+                      <span>
+                        {{ $t('R-GlobalCurrency-Update') }}
+                      </span>
+                    </el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="card-mock-demo" style="margin-bottom: 30px">
+        <el-table :data="mock_tableData" stripe style="margin-top: 20px; width: 100%">
+          <el-table-column prop="payoutCurrency" align="left" :label="$t('global.p1')">
+          </el-table-column>
+          <el-table-column prop="transferTypeValue" align="left" :label="$t('global.p2')" />
+          <el-table-column prop="payoutMethodValue" align="left" :label="$t('global.p3')">
+            <template #default="scope">
+              <span>{{ $t(payoutMethod[scope.row.payoutMethodId]) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="country" align="left" :label="$t('global.p4')" />
+          <el-table-column prop="feeRateConfig" align="left" :label="$t('global.p5')" />
+          <el-table-column prop="fixedFeeConfig" align="left" :label="$t('global.p6')" />
+          <el-table-column prop="minQuotaConfig" align="left" :label="$t('global.p7')" />
+          <el-table-column prop="maxQuotaConfig" align="left" :label="$t('global.p8')" />
+          <el-table-column
+            prop="yearTransferAmountQuotaConfig"
+            align="left"
+            :label="$t('global.p9')"
+          />
+
+          <el-table-column prop="status" align="left" :label="$t('Ucard.CardType.item7')">
+            <template #default="scope">
+              <el-tag :type="scope.row.status === 'online' ? 'success' : 'info'"
+                >{{
+                  scope.row.status === 'online' ? $t('Ucard.CardType.t6') : $t('Ucard.CardType.t5')
+                }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column :label="$t('Ucard.Business.item20')" align="center">
+            <template #default="scope">
+              <el-dropdown trigger="click" @command="handleCommand">
+                <span class="el-dropdown-link crm-cursor">
+                  <i style="font-weight: bold; font-size: 20px" class="iconfont iconcaidan"></i>
+                </span>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item
+                      v-if="
+                        display['R-GlobalCurrency-Update1'] &&
+                        display['R-GlobalCurrency-Update1'].show
+                      "
+                      :command="{ type: 2, row: scope.row }"
+                    >
+                      <el-icon><Edit /></el-icon>
+                      <span>
+                        {{ $t('R-GlobalCurrency-Update1') }}
+                      </span>
+                    </el-dropdown-item>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+    <PagePagination
+      :pager-info="pagerInfo"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange"
+    />
+    <el-dialog
+      v-model="approvalAllDialog"
+      :title="form.type == 1 ? $t('R-GlobalCurrency-Update') : $t('R-GlobalCurrency-Update1')"
+      width="500px"
+      :close-on-click-modal="false"
+    >
+      <el-form
+        ref="formRef"
+        :rules="rules"
+        :model="form"
+        label-position="top"
+        class="business-edit-form"
+      >
+        <el-form-item prop="feeRateConfig" :label="$t('global.p5')">
+          <el-input-number
+            v-model="form.feeRateConfig"
+            :controls="false"
+            :disabled-scientific="true"
+            :placeholder="$t('Placeholder.Input')"
+          />
+        </el-form-item>
+        <el-form-item prop="fixedFeeConfig" :label="$t('global.p6')">
+          <el-input-number
+            v-model="form.fixedFeeConfig"
+            :controls="false"
+            :placeholder="$t('Placeholder.Input')"
+          />
+        </el-form-item>
+        <!--      判断一下全局不用展示下面的,普通的需要展示 乘以汇率比对config的数值 -->
+        <template v-if="form.type == '2'">
+          <el-form-item prop="minQuotaConfig">
+            <template #label>
+              <span>
+                <span>{{ $t('global.p7') }}</span>
+                <span v-if="minRate" class="rate-tip">{{
+                  `${$t('Ucard.GlobalOrder.currencyTip')}${minRate}`
+                }}</span>
+              </span>
+            </template>
+            <el-input
+              v-model="form.minQuotaConfig"
+              type="number"
+              :max="form.maxMinQuota"
+              :min="form.maxQuotaConfig"
+              :placeholder="$t('Placeholder.Input')"
+            />
+          </el-form-item>
+          <el-form-item prop="maxQuotaConfig">
+            <template #label>
+              <span>
+                <span>{{ $t('global.p8') }}</span>
+                <span v-if="maxRate" class="rate-tip">{{
+                  `${$t('Ucard.GlobalOrder.currencyTip')} ${maxRate}`
+                }}</span>
+              </span>
+            </template>
+            <el-input
+              v-model="form.maxQuotaConfig"
+              type="number"
+              :placeholder="$t('Placeholder.Input')"
+            />
+          </el-form-item>
+          <el-form-item prop="yearTransferAmountQuotaConfig" :label="$t('global.p9')">
+            <template #label>
+              <span>
+                <span>{{ $t('global.p9') }}</span>
+                <span v-if="yearRate" class="rate-tip">{{
+                  `${$t('Ucard.GlobalOrder.currencyTip')} ${yearRate}`
+                }}</span>
+              </span>
+            </template>
+            <el-input-number
+              v-model="form.yearTransferAmountQuotaConfig"
+              type="number"
+              :controls="false"
+              :max="form.minYearTransferAmountQuota"
+              :placeholder="$t('Placeholder.Input')"
+            />
+          </el-form-item>
+        </template>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="approvalAllDialog = false">
+            {{ $t('Ucard.Business.p32') }}
+          </el-button>
+          <el-button type="primary" @click="updateCardTypesConfig">
+            {{ $t('card.Btn.Confirm') }}
+          </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+  import { computed, inject, onMounted, reactive, ref, watch } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import { ElMessage } from 'element-plus'
+  import { multiply } from 'lodash-es'
+  import Service from '@/service/ucard'
+  import Config from '@/config/index'
+  import PagePagination from '@/components/PagePagination'
+  import { payoutMethod, transferType } from './const'
+  import { floatReg } from '@/utils/const'
+  import { Edit } from '@element-plus/icons-vue'
+
+  const { Code } = Config
+
+  const { t } = useI18n()
+
+  const Session = inject('session')
+  const pigeon = inject('pigeon')
+
+  // 响应式数据
+  const pictLoading = ref(false)
+  const syncLoading = ref(false)
+  const approvalAllDialog = ref(false)
+
+  const search = reactive({
+    status: '',
+    payoutCurrency: '',
+    transferTypeId: '',
+    payoutMethodId: '',
+  })
+
+  const form = reactive({
+    feeRateConfig: null,
+    fixedFeeConfig: null,
+    minQuotaConfig: null,
+    maxQuotaConfig: null,
+    yearTransferAmountQuotaConfig: null,
+  })
+
+  const currenciesDropdown = ref([])
+  const mergeFirstRowData = ref([])
+  const mock_tableData = ref([])
+
+  const pagerInfo = reactive({
+    row: 10,
+    current: 1,
+    pageTotal: 0,
+    rowTotal: 0,
+  })
+
+  const minRate = ref(0)
+  const maxRate = ref(0)
+  const yearRate = ref(0)
+
+  const formRef = ref()
+  // 搜索
+  const searchForm = ref()
+  // 计算属性
+  const display = computed(() => {
+    return JSON.parse(Session.Get('display', true))
+  })
+
+  // 表单验证规则
+  const rules = computed(() => ({
+    feeRateConfig: [
+      {
+        validator: (rule, value, callback) => {
+          if (value) {
+            const test = floatReg.test(value)
+            if (test) {
+              callback()
+            } else {
+              callback(new Error(t('global.validator.v13')))
+            }
+          }
+          callback()
+        },
+        trigger: 'change',
+      },
+    ],
+    fixedFeeConfig: [
+      {
+        validator: (rule, value, callback) => {
+          if (value) {
+            const test = floatReg.test(value)
+            if (test) {
+              callback()
+            } else {
+              callback(new Error(t('global.validator.v13')))
+            }
+          }
+          callback()
+        },
+        trigger: 'change',
+      },
+    ],
+    minQuotaConfig: [
+      {
+        validator: (rule, value, callback) => {
+          if (form.type == 1 || !value) {
+            return callback()
+          }
+          const minQuota = Number(form.minQuota)
+          const maxQuota = Number(form.maxQuota)
+          const maxConfig = Number(form.maxQuotaConfig)
+          const exchangeRate = Number(form.exchangeRate)
+
+          const test = floatReg.test(value)
+          if (!test) {
+            return callback(new Error(t('global.validator.v13')))
+          }
+
+          value = Number(value)
+          if (isNaN(value)) {
+            return callback(new Error(t('global.validator.v12')))
+          }
+
+          const val = multiply(value, exchangeRate)
+          minRate.value = val
+
+          if (val < minQuota) {
+            callback(new Error(t('global.validator.v2', { minQuota })))
+          } else if (val > maxQuota) {
+            callback(new Error(t('global.validator.v3', { maxQuota })))
+          } else if (maxConfig && value > maxConfig) {
+            callback(new Error(t('global.validator.v4', { maxConfig })))
+          } else {
+            callback()
+          }
+        },
+        trigger: 'change',
+      },
+    ],
+    maxQuotaConfig: [
+      {
+        validator: (rule, value, callback) => {
+          if (form.type == 1 || !value) {
+            return callback()
+          }
+          const minQuota = Number(form.minQuota)
+          const maxQuota = Number(form.maxQuota)
+          const minConfig = Number(form.minQuotaConfig)
+          const exchangeRate = Number(form.exchangeRate)
+
+          const test = floatReg.test(value)
+          if (!test) {
+            return callback(new Error(t('global.validator.v13')))
+          }
+
+          value = Number(value)
+          if (isNaN(value)) {
+            return callback(new Error(t('global.validator.v12')))
+          }
+
+          const val = multiply(value, exchangeRate)
+          maxRate.value = val
+
+          if (val > maxQuota) {
+            callback(new Error(t('global.validator.v6', { maxQuota })))
+          } else if (val < minQuota) {
+            callback(new Error(t('global.validator.v7', { minQuota })))
+          } else if (minConfig && value < minConfig) {
+            callback(new Error(t('global.validator.v8', { minConfig })))
+          } else {
+            callback()
+          }
+        },
+        trigger: 'change',
+      },
+    ],
+    yearTransferAmountQuotaConfig: [
+      {
+        validator: (rule, value, callback) => {
+          if (form.type == 1 || !value) {
+            return callback()
+          }
+          const yearMax = Number(form.yearTransferAmountQuota)
+          const maxConfig = Number(form.maxQuotaConfig)
+          const exchangeRate = Number(form.exchangeRate)
+
+          const test = floatReg.test(value)
+          if (!test) {
+            return callback(new Error(t('global.validator.v13')))
+          }
+
+          value = Number(value)
+          if (isNaN(value)) {
+            return callback(new Error(t('global.validator.v12')))
+          }
+
+          const val = multiply(value, exchangeRate)
+          yearRate.value = val
+
+          if (val > yearMax) {
+            callback(new Error(t('global.validator.v10', { yearMax })))
+          } else if (maxConfig && val < maxConfig) {
+            callback(new Error(t('global.validator.v11', { maxConfig })))
+          } else {
+            callback()
+          }
+        },
+        trigger: 'change',
+      },
+    ],
+  }))
+
+  // 方法
+  const keydown = (e, s) => {
+    console.log(e, s, 'key')
+  }
+
+  const handleCommand = async (command) => {
+    console.log(command)
+    switch (command.type) {
+      case 1: {
+        const data = {
+          ...command.row,
+          type: '1',
+        }
+        Object.assign(form, data)
+        approvalAllDialog.value = true
+        break
+      }
+      case 2: {
+        let data = {
+          ...command.row,
+          type: '2',
+        }
+
+        const { payoutCurrency, payoutMethodId, transferTypeId } = data
+        const res = await Service.globalLatestExchangeRate({
+          payoutCurrency,
+          payoutMethodId,
+          transferTypeId,
+        })
+
+        if (res.code == Code.StatusOK) {
+          data = {
+            ...data,
+            exchangeRate: res.data,
+          }
+        }
+
+        Object.assign(form, data)
+        console.log(form.type)
+        approvalAllDialog.value = true
+        break
+      }
+    }
+  }
+
+  const updateCardTypesConfig = async () => {
+    try {
+      // 注意:这里需要获取实际的表单引用,假设 formRef 已经绑定到模板
+      if (!formRef.value) return
+      console.log(formRef)
+      const valid = await formRef.value.validate()
+
+      if (!valid) return
+
+      const res = await Service.updateGlobalFee({ ...form })
+      if (res.code == Code.StatusOK) {
+        Object.assign(form, {
+          feeRateConfig: null,
+          fixedFeeConfig: null,
+          minQuotaConfig: null,
+          maxQuotaConfig: null,
+          yearTransferAmountQuotaConfig: null,
+        })
+        approvalAllDialog.value = false
+        toSearch()
+      } else {
+        pigeon.MessageError(res.msg)
+      }
+    } catch (error) {
+      console.log(error, 1212)
+    }
+  }
+
+  const closeForm = () => {
+    minRate.value = 0
+    maxRate.value = 0
+    yearRate.value = 0
+  }
+
+  const toSearch = () => {
+    pagerInfo.current = 1
+    globalCurrenciesConfig()
+    searchFunc()
+  }
+
+  const updateCardTypes = async () => {
+    syncLoading.value = true
+    const res = await Service.globalCurrenciesSave({})
+
+    if (res.code == Code.StatusOK) {
+      pigeon.MessageOK(t('Msg.SearchSuccess'))
+      searchFunc()
+    } else {
+      ElMessage.error(res.msg)
+    }
+    syncLoading.value = false
+  }
+
+  const updateCardType = () => {
+    approvalAllDialog.value = true
+  }
+
+  const globalCurrenciesDropdown = async () => {
+    const res = await Service.globalCurrenciesDropdown({})
+    if (res.code == Code.StatusOK) {
+      currenciesDropdown.value = [...new Set(res.data.map((item) => item.payoutCurrency))]
+    } else {
+      ElMessage.error(res.msg)
+    }
+  }
+
+  const globalCurrenciesConfig = async () => {
+    const res = await Service.globalCurrenciesConfig({})
+    if (res.code == Code.StatusOK) {
+      mergeFirstRowData.value = [res.data]
+    } else {
+      ElMessage.error(res.msg)
+    }
+  }
+
+  const searchFunc = async () => {
+    pictLoading.value = true
+
+    if (!display.value['R-GlobalCurrency-List'].show) {
+      ElMessage.warning(t('Msg.NotDisplay'))
+      pictLoading.value = false
+      return
+    }
+
+    const res = await Service.globalCurrenciesList({
+      ...search,
+      page: {
+        current: pagerInfo.current,
+        row: pagerInfo.row,
+      },
+    })
+
+    if (res.code == Code.StatusOK) {
+      mock_tableData.value = res.data
+      if (res.page != null) {
+        pagerInfo.rowTotal = res.page.rowTotal
+        pagerInfo.pageTotal = res.page.pageTotal
+      } else {
+        pagerInfo.rowTotal = 0
+      }
+      ElMessage.success(t('Msg.SearchSuccess'))
+    } else {
+      ElMessage.error(res.msg)
+    }
+    pictLoading.value = false
+  }
+
+  const handleSizeChange = (val) => {
+    pagerInfo.row = val
+    searchFunc()
+  }
+
+  const handleCurrentChange = (val) => {
+    pagerInfo.current = val
+    searchFunc()
+  }
+
+  // 生命周期
+  onMounted(() => {
+    globalCurrenciesConfig()
+    searchFunc()
+    globalCurrenciesDropdown()
+  })
+
+  // 侦听器
+  watch(approvalAllDialog, (newVal) => {
+    if (!newVal) {
+      closeForm()
+    }
+  })
+</script>
+<style scoped lang="scss">
+  #review_Email {
+    .el-table .state {
+      display: inline-block;
+      min-width: 80px;
+      max-width: 150px;
+      box-sizing: border-box;
+      line-height: 1.5;
+      border-radius: 2px;
+      padding: 2px 10px;
+      color: #ffffff;
+    }
+  }
+</style>
+<style scoped lang="scss">
+  #review_Email {
+    .dialog_header_w {
+      .crm_search_down {
+        width: 400px;
+      }
+    }
+
+    :deep(.business-edit-form) {
+      .el-form-item__label {
+        width: 100%;
+        text-align: left;
+        padding: 0;
+      }
+
+      .el-form-item {
+        width: 100%;
+        text-align: left;
+        padding: 0;
+      }
+
+      .el-input,
+      .el-select,
+      .el-date-picker,
+      .el-date-editor {
+        width: 100%;
+      }
+
+      .el-form-item__label {
+        font-weight: 500;
+      }
+
+      .el-row {
+        margin-bottom: 0;
+      }
+
+      .el-col {
+        margin-bottom: 0;
+      }
+    }
+
+    .rate-tip {
+      margin-left: 10px;
+      color: #999;
+      font-size: 12px;
+    }
+  }
+</style>

+ 24 - 0
src/views/card/VirtualCard/index.scss

@@ -0,0 +1,24 @@
+#review_Email {
+  .crm_search {
+    .search_action_btn {
+      .delete {
+        background-color: #a1a1a1;
+      }
+
+      .delete.active {
+        background-color: #368fec;
+      }
+    }
+  }
+
+  .el-table .state {
+    display: inline-block;
+    min-width: 80px;
+    max-width: 150px;
+    box-sizing: border-box;
+    line-height: 1.5;
+    border-radius: 2px;
+    padding: 2px 10px;
+    color: #ffffff;
+  }
+}

+ 860 - 0
src/views/card/VirtualCard/index.vue

@@ -0,0 +1,860 @@
+<template>
+  <div
+    id="review_Email"
+    v-loading="pictLoading"
+    class="view"
+    :element-loading-background="loadingBackground"
+  >
+    <div class="crm_search">
+      <el-form ref="formRef" label-position="" :model="search" label-width="">
+        <el-row>
+          <el-col :span="24" :md="24" :lg="24">
+            <el-form-item>
+              <el-select
+                v-model="search.tag"
+                class="crm_search_down crm-border-radius-no"
+                :placeholder="$t('Placeholder.Choose')"
+              >
+                <el-option :label="$t('Label.CidAccount')" :value="1"></el-option>
+                <el-option :label="$t('Ucard.KycAuth.item2')" :value="2"></el-option>
+                <el-option :label="$t('Ucard.KycAuth.item3')" :value="3"></el-option>
+                <el-option :label="$t('Ucard.VirtualCard.s1')" :value="4"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-input
+                v-if="search.tag == 1"
+                v-model.trim="search.cId"
+                class="crm-border-left-no crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+              <el-input
+                v-if="search.tag == 2"
+                v-model.trim="search.mobile"
+                class="crm-border-left-no crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+              <el-input
+                v-if="search.tag == 3"
+                v-model.trim="search.email"
+                class="crm-border-left-no crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+              <el-input
+                v-if="search.tag == 4"
+                v-model.trim="search.cardNumber"
+                class="crm-border-left-no crm-border-radius-no"
+                clearable
+                :placeholder="$t('Placeholder.Input')"
+                @keyup.enter="toSearch"
+              ></el-input>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-select
+                v-model="search.status"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('Ucard.VirtualCard.s2')"
+                @change="toSearch"
+              >
+                <el-option :label="$t('Ucard.VirtualCard.t9')" value="unactivate"></el-option>
+                <el-option :label="$t('Ucard.VirtualCard.t11')" value="wait_process"></el-option>
+                <el-option :label="$t('Ucard.VirtualCard.t5')" value="processing"></el-option>
+                <el-option :label="$t('Ucard.VirtualCard.t6')" value="success"></el-option>
+                <el-option :label="$t('Ucard.VirtualCard.t10')" value="fail"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-select
+                v-model="search.freezeStatus"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('Ucard.VirtualCard.item6')"
+                @change="toSearch"
+              >
+                <el-option :label="$t('Ucard.VirtualCard.t7')" :value="1"></el-option>
+                <el-option :label="$t('card.Btn.b22')" :value="2"></el-option>
+                <el-option :label="$t('Ucard.VirtualCard.t8')" :value="3"></el-option>
+                <el-option :label="$t('card.Btn.b20')" :value="4"></el-option>
+                <el-option :label="$t('card.Btn.b21')" :value="5"></el-option>
+                <el-option :label="$t('card.Btn.b19')" :value="6"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item style="margin-right: 10px">
+              <el-select
+                v-model="search.blockedStatus"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('card.Btn.b24')"
+                @change="toSearch"
+              >
+                <el-option :label="$t('Ucard.VirtualCard.t7')" :value="1"></el-option>
+                <el-option :label="$t('card.Btn.b18')" :value="2"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-select
+                v-model="search.cardStatus"
+                class="crm-border-radius-no"
+                clearable
+                :placeholder="$t('card.Btn.b23')"
+                @change="toSearch"
+              >
+                <el-option :label="$t('Ucard.VirtualCard.t7')" :value="1"></el-option>
+                <el-option :label="$t('card.Btn.b17')" :value="2"></el-option>
+              </el-select>
+            </el-form-item>
+            <el-form-item>
+              <el-button class="crm-border-radius-no crm-border-left-no" @click="toSearch">
+                <el-icon><Search /></el-icon>
+              </el-button>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item>
+          <el-button
+            v-if="display['R-VirtualCard-Export'] && display['R-VirtualCard-Export'].show"
+            type="primary"
+            style="margin-left: 8px"
+            @click="setExport"
+            >{{ $t('Btn.Export') }}</el-button
+          >
+        </el-form-item>
+      </el-form>
+      <div class="card-mock-demo" style="margin: 30px 0">
+        <el-table :data="mock_tableData" stripe style="margin-top: 20px; width: 100%">
+          <el-table-column prop="" align="left" :label="$t('Label.CidAccount')">
+            <template #default="scope">
+              <span
+                v-if="scope.row.cId && display['R-VirtualCard-Btn1'].show"
+                class="crm-text-underline"
+                @click="accountOpen(scope.row.cId)"
+                >{{ scope.row.cId || '--' }}</span
+              >
+              <span v-else>{{ scope.row.cId || '--' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="" align="left" :label="$t('Label.Name')">
+            <template #default="scope">
+              <span v-if="scope.row.firstName">{{ scope.row.firstName + ' ' }}</span>
+              <span v-if="scope.row.middle">{{ scope.row.middle + ' ' }}</span>
+              <span v-if="scope.row.lastName">{{ scope.row.lastName }}</span>
+              <span v-if="!scope.row.firstName && !scope.row.lastName && !scope.row.middle"
+                >--</span
+              >
+            </template>
+          </el-table-column>
+          <el-table-column prop="" align="left" :label="$t('Label.Email')">
+            <template #default="scope">
+              {{ scope.row.email || '--' }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="mobile" align="left" :label="$t('Ucard.KycAuth.item2')">
+            <template #default="scope"> {{ scope.row.areaCode }} {{ scope.row.mobile }} </template>
+          </el-table-column>
+          <el-table-column prop="cardNumber" align="left" :label="$t('Ucard.VirtualCard.item2')">
+            <template #default="scope">
+              <span
+                v-if="scope.row.cardNumber"
+                class="crm-text-underline"
+                @click="cardOpen(scope.row.cardNumber)"
+                >{{ scope.row.cardNumber || '--' }}</span
+              >
+              <span v-else>{{ scope.row.cardNumber || '--' }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="expireDate" align="left" :label="$t('Ucard.VirtualCard.item3')" />
+          <el-table-column prop="cvv" align="left" :label="$t('Ucard.VirtualCard.item4')">
+            <template #default="scope">
+              <span v-if="scope.row.expireDate" class="cvv" @click="getQueryCvv(scope.row.id)"
+                >*** <i class="el-icon-view"></i
+              ></span>
+            </template>
+          </el-table-column>
+
+          <el-table-column prop="type" align="left" :label="$t('Ucard.KycAuth.item1')" />
+          <el-table-column prop="status" align="left" :label="$t('Ucard.VirtualCard.item5')">
+            <template #default="scope">
+              <span v-if="scope.row.status === 'unactivate'" class="state crm_state_yellow">{{
+                $t('Ucard.VirtualCard.t9')
+              }}</span>
+              <span v-else-if="scope.row.status === 'success'" class="state crm_state_blue">{{
+                $t('Ucard.VirtualCard.t6')
+              }}</span>
+              <span v-else-if="scope.row.status === 'fail'" class="state crm_state_gray">{{
+                $t('Ucard.VirtualCard.t10')
+              }}</span>
+              <span
+                v-else-if="scope.row.status === 'wait_process'"
+                class="state crm_state_yellow"
+                >{{ $t('Ucard.VirtualCard.t11') }}</span
+              >
+              <span v-else class="state crm_state_orange">{{ $t('Ucard.VirtualCard.t5') }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="freezeStatus" align="left" :label="$t('Ucard.VirtualCard.item6')">
+            <template #default="scope">
+              <template v-if="scope.row.freezeType == 1">
+                <span v-if="scope.row.freezeStatus == 'success'" class="state crm_state_blue">{{
+                  $t('Ucard.VirtualCard.t7')
+                }}</span>
+                <span v-else-if="scope.row.freezeStatus == 'fail'" class="state crm_state_gray">{{
+                  $t('card.Btn.b19')
+                }}</span>
+                <span v-else class="state crm_state_orange">{{ $t('card.Btn.b21') }}</span>
+              </template>
+              <template v-else>
+                <span v-if="scope.row.freezeStatus == 'success'" class="state crm_state_red">{{
+                  $t('Ucard.VirtualCard.t8')
+                }}</span>
+                <span v-else-if="scope.row.freezeStatus == 'fail'" class="state crm_state_gray">{{
+                  $t('card.Btn.b20')
+                }}</span>
+                <span v-else class="state crm_state_orange">{{ $t('card.Btn.b22') }}</span>
+              </template>
+            </template>
+          </el-table-column>
+          <el-table-column prop="blocked" align="left" :label="$t('card.Btn.b24')">
+            <template #default="scope">
+              <span v-if="!scope.row.blocked" class="state crm_state_blue">{{
+                $t('Ucard.VirtualCard.t7')
+              }}</span>
+              <span v-else class="state crm_state_red">{{ $t('card.Btn.b18') }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="cardStatus" align="left" :label="$t('card.Btn.b23')">
+            <template #default="scope">
+              <span v-if="scope.row.cardStatus != 'cancel'" class="state crm_state_blue">{{
+                $t('Ucard.VirtualCard.t7')
+              }}</span>
+              <span v-else class="state crm_state_red">{{ $t('card.Btn.b17') }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="" align="center" :label="$t('Label.Action')">
+            <template #default="scope">
+              <el-dropdown trigger="click" @command="handleCommand">
+                <span class="el-dropdown-link crm-cursor">
+                  <i style="font-weight: bold; font-size: 20px" class="iconfont iconcaidan"></i>
+                </span>
+                <template #dropdown>
+                  <el-dropdown-menu>
+                    <el-dropdown-item
+                      v-if="display['R-VirtualCard-Btn1'].show"
+                      :command="{ type: 0, row: scope.row }"
+                    >
+                      <el-icon><Operation /></el-icon>
+                      <span>{{ $t('R-VirtualCard-Btn1') }}</span>
+                    </el-dropdown-item>
+                    <template v-if="scope.row.cardStatus != 'cancel'">
+                      <el-dropdown-item
+                        v-if="
+                          display['R-VirtualCard-Btn3'].show &&
+                          scope.row.type == 'Physical' &&
+                          (scope.row.status === 'unactivate' || scope.row.status === 'fail')
+                        "
+                        :command="{ type: 9, row: scope.row }"
+                      >
+                        <el-icon><Operation /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn3') }}</span>
+                      </el-dropdown-item>
+                      <el-dropdown-item
+                        v-if="
+                          display['R-VirtualCard-Btn10'].show && scope.row.status == 'processing'
+                        "
+                        :command="{ type: 11, row: scope.row }"
+                      >
+                        <el-icon><Operation /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn10') }}</span>
+                      </el-dropdown-item>
+                      <el-dropdown-item
+                        v-if="
+                          display['R-VirtualCard-Btn8'].show &&
+                          (scope.row.status == 'unactivate' || scope.row.status == 'fail')
+                        "
+                        :command="{ type: 1, row: scope.row }"
+                      >
+                        <el-icon><Operation /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn8') }}</span>
+                      </el-dropdown-item>
+                      <el-dropdown-item
+                        v-if="display['R-VirtualCard-Btn9'].show"
+                        :command="{ type: 10, row: scope.row }"
+                      >
+                        <el-icon><Refresh /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn9') }}</span>
+                      </el-dropdown-item>
+                      <el-dropdown-item
+                        v-if="display['R-VirtualCard-Btn2'].show"
+                        :command="{ type: 2, row: scope.row }"
+                      >
+                        <el-icon><Operation /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn2') }}</span>
+                      </el-dropdown-item>
+                      <el-dropdown-item
+                        v-if="display['R-VirtualCard-Btn11'].show"
+                        :command="{ type: 13, row: scope.row }"
+                      >
+                        <el-icon><Operation /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn11') }}</span>
+                      </el-dropdown-item>
+                      <el-dropdown-item
+                        v-if="display['R-VirtualCard-Btn4'].show"
+                        :command="{ type: 5, row: scope.row }"
+                      >
+                        <el-icon><Refresh /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn4') }}</span>
+                      </el-dropdown-item>
+                      <el-dropdown-item
+                        v-if="display['R-VirtualCard-Btn5'].show"
+                        :command="{ type: 6, row: scope.row }"
+                      >
+                        <el-icon><Operation /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn5') }}</span>
+                      </el-dropdown-item>
+                      <el-dropdown-item
+                        v-if="display['R-VirtualCard-Btn6'].show"
+                        :command="{ type: 7, row: scope.row }"
+                      >
+                        <el-icon><Operation /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn6') }}</span>
+                      </el-dropdown-item>
+                      <el-dropdown-item
+                        v-if="display['R-VirtualCard-Btn7'].show"
+                        :command="{ type: 8, row: scope.row }"
+                      >
+                        <el-icon><CircleCheck /></el-icon>
+                        <span>{{ $t('R-VirtualCard-Btn7') }}</span>
+                      </el-dropdown-item>
+                    </template>
+                  </el-dropdown-menu>
+                </template>
+              </el-dropdown>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </div>
+    <div v-if="pagerInfo.rowTotal" class="crm_pagination">
+      <div class="crm_page_total">
+        <span>{{ $t('Page.total.item1') }}</span>
+        <span>{{ pagerInfo.rowTotal }}</span>
+        <span>{{ $t('Page.total.item2') }}</span>
+      </div>
+      <el-pagination
+        class="page"
+        background
+        layout="sizes, prev, pager, next"
+        :page-sizes="[10, 20, 50, 100]"
+        :page-size="pagerInfo.row"
+        :total="pagerInfo.rowTotal"
+        @current-change="handleCurrentChange"
+        @size-change="handleSizeChange"
+      >
+      </el-pagination>
+    </div>
+    <trading-info-add
+      :dialog-info-trading-add="dialogInfoTradingAdd"
+      :editor="editor"
+      :add-type="addType"
+      :my-info="myInfo"
+      :form-list="formList"
+      @confirm-to-reload="confirmToReload"
+      @close-add="closeAdd"
+    ></trading-info-add>
+    <div v-if="dialogInfoTradingAdd" class="crm_verified_info_mask" @click="closeDiaAdd"></div>
+    <detailed-info-cid
+      :dialog-info-cid="dialogInfoCid"
+      :form-info="formInfo"
+      :is-trading="true"
+      @close="close"
+    >
+    </detailed-info-cid>
+    <div v-if="dialogInfoCid" class="crm_verified_info_mask" @click="closeDia"></div>
+    <el-dialog
+      v-model="dialogCheck"
+      :title="$t('Ucard.VirtualCard.d5')"
+      center
+      class="dialog_header_w"
+    >
+      <div class="dia-content">
+        <el-table :data="formList.data" style="width: 100%">
+          <el-table-column prop="currency" :label="$t('Ucard.VirtualCard.item9')">
+          </el-table-column>
+          <el-table-column prop="amount" :label="$t('Ucard.VirtualCard.item10')"> </el-table-column>
+        </el-table>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="cancel">{{ $t('Btn.Cancel') }}</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog v-model="dialogCheckCvv" title="CVV" center class="dialog_header_w">
+      <div class="dia-content">
+        <el-form
+          :rules="rules"
+          :model="cvvForm"
+          label-width="130px"
+          label-position="right"
+          class="business-edit-form"
+        >
+          <el-form-item prop="gaCode" :label="$t('getCode.R-GoogleSecretKey')">
+            <el-input v-model="cvvForm.gaCode" :placeholder="$t('Placeholder.Input')"></el-input>
+          </el-form-item>
+          <el-form-item v-if="cvvForm.cvv" prop="cvv" label="CVV">
+            <el-input v-model="cvvForm.cvv" :placeholder="$t('Placeholder.Input')"></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="cancel1">{{ $t('Btn.Cancel') }}</el-button>
+          <el-button v-if="!cvvForm.cvv" type="primary" @click="queryCvv()">{{
+            $t('Btn.Confirm')
+          }}</el-button>
+          <el-button v-if="cvvForm.cvv" type="primary" @click="copy()">{{
+            $t('Label.Copy')
+          }}</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <el-dialog
+      v-model="dialogCheckExport"
+      :title="$t('getCode.R-GoogleSecretKey')"
+      center
+      class="dialog_header_w"
+    >
+      <div class="dia-content">
+        <el-form
+          :rules="rules"
+          :model="cvvForm"
+          label-width="130px"
+          label-position="right"
+          class="business-edit-form"
+        >
+          <el-form-item prop="gaCode" :label="$t('getCode.R-GoogleSecretKey')">
+            <el-input v-model="cvvForm.gaCode" :placeholder="$t('Placeholder.Input')"></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="cancel1">{{ $t('Btn.Cancel') }}</el-button>
+          <el-button type="primary" @click="exportAgents()">{{ $t('Btn.Confirm') }}</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <ViewCardSingle
+      :dialog-info-trading-single="dialogInfoTradingSingle"
+      :form-list="formSingle"
+      :editor-type="editorType"
+      @close-single="closeSingle"
+    >
+    </ViewCardSingle>
+    <div v-if="dialogInfoTradingSingle" class="crm_verified_info_mask" @click="closeSingle"></div>
+  </div>
+</template>
+
+<script setup>
+  import { ref, reactive, computed, onMounted, watch, inject } from 'vue'
+  import { useRouter } from 'vue-router'
+  import Service from '@/service/ucard'
+  import TradingInfoAdd from '@/views/components/VirtualCard'
+  import Config from '@/config/index'
+  import Service1 from '@/service/customer'
+  import DetailedInfoCid from '@/views/components/DetailedInfoCid'
+  import ViewCardSingle from '@/views/components/ViewCardSingle'
+  import { exportExcel } from '@/utils/export'
+  import { CircleCheck, Operation, Refresh, Search } from '@element-plus/icons-vue'
+  import { useI18n } from 'vue-i18n'
+  import { copyText } from '@/utils/untils'
+
+  const { t } = useI18n()
+  const router = useRouter()
+  const Session = inject('session')
+  const pigeon = inject('pigeon')
+  const { Code } = Config
+
+  // 响应式数据
+  const pictLoading = ref(false)
+  const dialogInfoTradingAdd = ref(false)
+  const dialogInfoTradingSingle = ref(false)
+  const formSingle = ref({})
+  const editorType = ref(5)
+  const dialogCheck = ref(false)
+  const dialogCheckCvv = ref(false)
+  const dialogCheckExport = ref(false)
+  const formRef = ref(null)
+
+  const search = reactive({
+    tag: 1,
+    cardNumber: '',
+    email: '',
+    mobile: '',
+    cId: '',
+    status: '',
+    freezeStatus: '',
+    blockedStatus: '',
+    cardStatus: '',
+  })
+
+  const editor = ref('')
+  const addType = ref('')
+  const myInfo = ref({})
+  const formList = ref({})
+  const mock_tableData = ref([])
+  const pagerInfo = reactive({ row: 10, current: 1, pageTotal: 0, rowTotal: 0 })
+  const dialogInfoCid = ref(false)
+  const formInfo = ref({})
+
+  const cvvForm = reactive({
+    gaCode: undefined,
+    cvv: undefined,
+    id: undefined,
+  })
+
+  const rules = reactive({
+    gaCode: [
+      {
+        required: true,
+        message: t('vaildate.input.empty'),
+        trigger: 'blur',
+      },
+    ],
+  })
+
+  const loadingBackground = 'rgba(43, 48, 67, 0.65)'
+
+  // 计算属性
+  const display = computed(() => {
+    return JSON.parse(Session.Get('display', true))
+  })
+
+  const user = computed(() => {
+    return JSON.parse(Session.Get('user', true))
+  })
+
+  // 方法
+  const setExport = () => {
+    cvvForm.gaCode = undefined
+    dialogCheckExport.value = true
+  }
+
+  const exportAgents = async () => {
+    exportExcel(pigeon, '/wasabi/card/list/export', { ...search }, 'Bank_Cards_List')
+  }
+
+  const accountOpen = (cId) => {
+    router.push({ name: 'R-CustomerDetail', params: { cId: cId } })
+  }
+
+  const cardOpen = (cardNumber) => {
+    router.push({
+      name: 'R-CardDetail',
+      params: { cardNumber: cardNumber },
+    })
+  }
+
+  const searchSingleCid = async (cId) => {
+    let res = await Service1.cidRealSingle({
+      id: cId,
+    })
+    if (res.code == Code.StatusOK) {
+      if (editor.value) {
+        formList.value = res.data
+        // dialogInfoAdd.value = true
+      } else {
+        formInfo.value = res.data
+        searchRealAll(cId)
+      }
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+  }
+
+  const tableRowClassName = ({ row }) => {
+    if (row.cardStatus === 'cancel' || row.blocked) {
+      return 'row-cancel'
+    }
+    return ''
+  }
+
+  const searchRealAll = async (cId) => {
+    let res = await Service1.realCustomerListAll({
+      cId: cId,
+      page: {
+        current: 1,
+        row: 1,
+      },
+    })
+    if (res.code == Code.StatusOK) {
+      formInfo.value.realList = res.data
+      dialogInfoCid.value = true
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+  }
+
+  const closeDia = () => {
+    dialogInfoCid.value = false
+  }
+
+  const close = (val) => {
+    dialogInfoCid.value = val
+  }
+
+  const cancel = () => {
+    dialogCheck.value = false
+  }
+
+  const cancel1 = () => {
+    dialogCheckCvv.value = false
+    dialogCheckExport.value = false
+  }
+
+  const closeSingle = () => {
+    dialogInfoTradingSingle.value = false
+  }
+
+  const handleCommand = (command) => {
+    if (
+      command.row.status == 'unactivate' &&
+      command.type != 1 &&
+      command.type != 9 &&
+      command.type != 0
+    ) {
+      pigeon.MessageOK(t('card.Info.t25'))
+      return
+    }
+    if (command.type != 0 && command.row.cardStatus == 'cancel') {
+      pigeon.MessageOK(t('card.New2.p5'))
+      return
+    }
+    if (command.type != 0 && command.row.blocked) {
+      pigeon.MessageOK(t('card.New2.p6'))
+      return
+    }
+    switch (command.type) {
+      case 0:
+        dialogInfoTradingSingle.value = true
+        formSingle.value = command.row
+        break
+      case 1:
+        if (command.row.status != 1) {
+          addType.value = 1
+          dialogInfoTradingAdd.value = true
+          formList.value = command.row
+        }
+        break
+      case 2:
+        addType.value = 2
+        dialogInfoTradingAdd.value = true
+        formList.value = command.row
+        break
+      case 3:
+        addType.value = 3
+        dialogInfoTradingAdd.value = true
+        formList.value = command.row
+        break
+      case 5:
+        ucardBalance(command.row)
+        break
+      case 6:
+        addType.value = 6
+        dialogInfoTradingAdd.value = true
+        formList.value = command.row
+        break
+      case 7:
+        addType.value = 7
+        dialogInfoTradingAdd.value = true
+        formList.value = { ...command.row, clientRemark: '' }
+        break
+      case 8:
+        addType.value = 8
+        dialogInfoTradingAdd.value = true
+        formList.value = { ...command.row, clientRemark: '' }
+        break
+      case 9:
+        addType.value = 9
+        dialogInfoTradingAdd.value = true
+        formList.value = command.row
+        break
+      case 10:
+        getCardInfo(command.row)
+        break
+      case 11:
+        rechargeUpdate(command.row)
+        break
+      case 13:
+        addType.value = 13
+        dialogInfoTradingAdd.value = true
+        formList.value = command.row
+        break
+    }
+  }
+
+  const closeAdd = () => {
+    formList.value = {}
+    dialogInfoTradingAdd.value = false
+  }
+
+  const closeDiaAdd = () => {
+    formList.value = {}
+    dialogInfoTradingAdd.value = false
+  }
+
+  const confirmToReload = () => {
+    closeDiaAdd()
+    searchFunc()
+  }
+
+  const toSearch = () => {
+    pagerInfo.current = 1
+    searchFunc()
+  }
+
+  const ucardBalance = async ({ uniqueId, cardNo }) => {
+    let res = await Service.ucardBalance({ uniqueId, cardNo })
+    if (res.code == Code.StatusOK) {
+      pigeon.MessageOK(t('Msg.SearchSuccess'))
+      formList.value.data = [res.data]
+      addType.value = 5
+      dialogCheck.value = true
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+  }
+
+  const getCardInfo = async ({ id }) => {
+    let res = await Service.getCardInfo({ id })
+    if (res.code == Code.StatusOK) {
+      toSearch()
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+  }
+
+  const getQueryCvv = (id) => {
+    dialogCheckCvv.value = true
+    Object.assign(cvvForm, {
+      gaCode: undefined,
+      cvv: undefined,
+      id,
+    })
+  }
+
+  const queryCvv = async () => {
+    let res = await Service.queryCvv({ ...cvvForm })
+    if (res.code == Code.StatusOK) {
+      cvvForm.cvv = res.data
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+  }
+
+  const copy = () => {
+    try {
+      copyText(cvvForm.cvv)
+      pigeon.MessageOK(t('Msg.Success15'))
+    } catch (err) {
+      pigeon.MessageError(t('Msg.Success16'))
+    }
+  }
+
+  const rechargeUpdate = async ({ id }) => {
+    let res = await Service.rechargeUpdate({ id })
+    if (res.code == Code.StatusOK) {
+      toSearch()
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+  }
+
+  const searchFunc = async () => {
+    pictLoading.value = true
+    if (!display.value['R-VirtualCard-Search'].show) {
+      pigeon.MessageWarning(t('Msg.NotDisplay'))
+      pictLoading.value = false
+      return
+    }
+    let res = await Service.cardList({
+      ...search,
+      page: {
+        current: pagerInfo.current,
+        row: pagerInfo.row,
+      },
+    })
+    if (res.code == Code.StatusOK) {
+      mock_tableData.value = res.data
+      if (res.page != null) {
+        pagerInfo.rowTotal = res.page.rowTotal
+        pagerInfo.pageTotal = res.page.pageTotal
+      } else {
+        pagerInfo.rowTotal = 0
+      }
+      pigeon.MessageOK(t('Msg.SearchSuccess'))
+    } else {
+      pigeon.MessageError(res.msg)
+    }
+    pictLoading.value = false
+  }
+
+  const handleSizeChange = (val) => {
+    pagerInfo.row = val
+    searchFunc()
+  }
+
+  const handleCurrentChange = (val) => {
+    pagerInfo.current = val
+    searchFunc()
+  }
+
+  // 监听器
+  watch(
+    () => search.tag,
+    () => {
+      search.cardNumber = ''
+      search.status = ''
+      search.mobile = ''
+      search.email = ''
+      search.cId = ''
+    }
+  )
+
+  // 生命周期
+  onMounted(() => {
+    searchFunc()
+  })
+</script>
+
+<style scoped lang="scss">
+  @import 'index.scss';
+</style>
+<style lang="scss">
+  #review_Email {
+    .dialog_header_w {
+      .crm_search_down {
+        width: 400px;
+      }
+    }
+    .row-cancel {
+      /* background-color: #e20101 !important; */
+      color: #e20101 !important;
+      .el-dropdown {
+        color: #e20101;
+      }
+    }
+    .cvv {
+      cursor: pointer;
+    }
+    /* .row-cancel:hover > td {
+      background-color: #e20101 !important;
+    } */
+  }
+</style>

+ 1 - 1
src/views/components/ConsumerShareLink/index.vue

@@ -5,7 +5,7 @@
       center
       :modal-append-to-body="false"
       :title="$t('Ib.Index.CreateLink')"
-      custom-class="dialog_header_w dialogLink"
+      class="dialog_header_w dialogLink"
     >
       <div class="dia-content">
         <div class="content" style="font-size: 14px; text-align: left">

+ 2 - 2
src/views/components/DetailedInfoCid/index.vue

@@ -546,7 +546,7 @@
       center
       append-to-body
       @close="cancel"
-      custom-class="dialog_header_w"
+      class="dialog_header_w"
     >
       <div class="dia-content">
         <el-form
@@ -622,7 +622,7 @@
       center
       append-to-body
       @close="cancel"
-      custom-class="dialog_header_w"
+      class="dialog_header_w"
     >
       <div class="dia-content">
         <el-form

+ 1 - 1
src/views/components/ReviewEmail/index.vue

@@ -4,7 +4,7 @@
     :title="addType === 'kyc_upload' ? '上传KYC附件' : $t('Marketing.Email')"
     :size="'25%'"
     :before-close="close"
-    custom-class="review-email-drawer"
+    class="review-email-drawer"
     :wrapper-closable="true"
     :modal-append-to-body="false"
     :append-to-body="true"

+ 0 - 26
src/views/home2/Home.vue

@@ -147,32 +147,6 @@
         </div>
       </template>
     </el-dialog>
-
-    <!-- qa弹出 -->
-    <el-dialog
-      v-model:visible="dialogCheckQA"
-      :title="'Q&A'"
-      center
-      width="80%"
-      custom-class="dialog_header_w dialog_header_qa"
-    >
-      <div class="dia-content">
-        <qa-list
-          v-if="qaType == 1 && dialogCheckQA"
-          @to-new="toNew"
-          @to-update="toUpdate"
-          @to-check="toCheck"
-        >
-        </qa-list>
-        <qa-add
-          v-if="qaType == 2 && dialogCheckQA"
-          :my-id-q-a="myId"
-          :type-q-a="type"
-          @cancel-qa="cancelQa"
-        >
-        </qa-add>
-      </div>
-    </el-dialog>
   </el-container>
 </template>
 

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است