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