AccountList.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. <template>
  2. <view class="container">
  3. <view class="row">
  4. <view class="col-12">
  5. <view class="border-0 card-header">
  6. <view class="d-flex flex-wrap gap-3 align-items-center justify-content-between mb-3">
  7. <h3 class="mb-0" v-t="'Custom.Index.AccountList'"></h3>
  8. <button type="button" class="btn btn-secondary btn-shadow waves-effect" @click="createAccount"><i
  9. class="fi fi-rr-plus"></i>
  10. <text v-t="'Custom.Index.AddAccount'" /></button>
  11. </view>
  12. </view>
  13. </view>
  14. <view class="col-lg-12">
  15. <view class="clearfix">
  16. <view class="card">
  17. <view class="card-header">
  18. <view class="nav nav-underline card-header-tabs" id="myTab" role="tablist">
  19. <view class="nav-item cwg-cursor" v-for="(tab, index) in tabs" :key="index"
  20. @click="cativeIndex = index">
  21. <view class="nav-link" :class="{ 'active': index === cativeIndex }">{{ tab.text }}
  22. </view>
  23. </view>
  24. </view>
  25. </view>
  26. <view class="card-body">
  27. <view class="tab-content">
  28. <view class="tab-pane fade show active" id="home" role="tabpanel"
  29. aria-labelledby="home-tab" tabindex="0">
  30. <view class="row">
  31. <AccountCard v-for="acc in accounts" :zhtype="cativeIndex"
  32. :key="acc.accountNumber" :account="acc" :is-grid-layout="isGridLayout"
  33. @action="handleAction" @copy="handleCopy"
  34. @change-password="handleChangePassword" />
  35. <view class="table-loading-mask">
  36. <uni-loading v-if="loading" />
  37. </view>
  38. <cwg-empty-state v-if="!loading && accounts.length == 0" />
  39. <DeleteAccountDialogs ref="deleteAccountDialogRef"
  40. v-model:visible="deleteAccountDialogVisible" />
  41. <cwg-improve-popup v-model:visible="dialogCheck" @confirm="confirm" />
  42. </view>
  43. </view>
  44. </view>
  45. </view>
  46. </view>
  47. </view>
  48. </view>
  49. </view>
  50. </view>
  51. </template>
  52. <script setup lang="ts">
  53. import { computed, ref, onMounted, watch } from 'vue';
  54. import { useI18n } from 'vue-i18n';
  55. const { t, locale } = useI18n();
  56. import useRouter from "@/hooks/useRouter";
  57. const router = useRouter();
  58. import { customApi } from '@/service/custom';
  59. import { userApi } from '@/api/user';
  60. import useUserStore from "@/stores/use-user-store";
  61. const userStore = useUserStore();
  62. import AccountCard from './AccountCard.vue'
  63. import DeleteAccountDialogs from './DeleteAccountDialogs.vue'
  64. import { useFilters } from '@/composables/useFilters'
  65. const { numberFormat, numberDecimal } = useFilters()
  66. const search = ref({ platform: 'MT4' })
  67. import useGlobalStore from '@/stores/use-global-store'
  68. const globalStore = useGlobalStore()
  69. const isDark = computed(() => globalStore.theme === 'dark')
  70. const isAfterJuly28 = () => {
  71. const now = new Date();
  72. const july28 = new Date(2025, 6, 28, 0, 0, 0); // 月份从0开始,所以7月是6
  73. return now >= july28;
  74. }
  75. const handleAction = (type) => { /* 处理交易/入金等 */ };
  76. const handleCopy = (text: string) => {
  77. uni.setClipboardData({
  78. data: text,
  79. success: function () {
  80. uni.showToast({
  81. title: t('Btn.item8'),
  82. icon: 'none',
  83. duration: 2000
  84. });
  85. }
  86. });
  87. }
  88. const handleChangePassword = () => { /* 跳转修改密码 */ };
  89. const typeMap = computed(() => ({
  90. 1: t('AccountType.ClassicAccount'),
  91. 2: t('AccountType.SeniorAccount'),
  92. 3: !isAfterJuly28() ? t('AccountType.AgencyAccount') : '',
  93. 5: t('AccountType.SpeedAccount'),
  94. 6: t('AccountType.SpeedAccount'),
  95. 7: t('AccountType.StandardAccount'),
  96. 8: t('AccountType.CentAccount')
  97. }));
  98. const cativeIndex = ref(0)
  99. const isGridLayout = ref(true)
  100. const tabs = computed(() => ([
  101. { value: 'real', text: t('vu.item1') },
  102. { value: 'demo', text: t('vu.item2') }
  103. ]))
  104. const toggleLayout = () => {
  105. isGridLayout.value = !isGridLayout.value
  106. }
  107. const tableRef = ref(null)
  108. const expanded = ref(null)
  109. const toggleRowExpand = (row) => {
  110. expanded.value = row.expanded ? row.rowIndex : null
  111. }
  112. const toggleExpand = (index) => {
  113. tableRef?.value.toggleRowExpand(index)
  114. }
  115. const createActionButtons = (row) => {
  116. console.log(row, 'row');
  117. return computed(() => [
  118. {
  119. icon: 'crm-circle-dollar-to-slot',
  120. text: t('Custom.Index.Deposit'),
  121. action: 'deposit',
  122. disabled: row.closeFunctions?.indexOf('1') !== -1,
  123. show: true,
  124. class: 'deposit-btn'
  125. },
  126. {
  127. icon: 'crm-credit-card',
  128. text: t('Custom.Index.Withdrawals'),
  129. action: 'withdraw',
  130. disabled: row.closeFunctions?.indexOf('2') !== -1,
  131. show: true,
  132. class: 'withdraw-btn'
  133. },
  134. {
  135. icon: 'crm-money-bill-transfer',
  136. text: t('Custom.Index.Transfer'),
  137. action: 'transfer',
  138. disabled: (
  139. row.closeFunctions?.indexOf('5') !== -1 ||
  140. row.closeFunctions?.indexOf('6') !== -1 ||
  141. row.closeFunctions?.indexOf('3') !== -1
  142. ),
  143. show: true,
  144. class: 'transfer-btn'
  145. },
  146. {
  147. icon: 'crm-gear',
  148. text: t('Custom.Index.Settings'),
  149. action: 'settings',
  150. disabled: row.closeFunctions?.indexOf('4') !== -1,
  151. show: true,
  152. class: 'settings-btn'
  153. },
  154. {
  155. icon: 'crm-award',
  156. text: t('standardRebate.item1'),
  157. action: 'standardRebate',
  158. disabled: false,
  159. show: row.type === 7,
  160. class: 'rebate-btn'
  161. }
  162. ])
  163. }
  164. // 处理按钮点击
  165. const handleActionBtn = (action, row) => {
  166. switch (action) {
  167. case 'deposit':
  168. toDeposit(row)
  169. break
  170. case 'withdraw':
  171. toWithdraw(row)
  172. break
  173. case 'transfer':
  174. toTransfer(row)
  175. break
  176. case 'settings':
  177. toSettings(row)
  178. break
  179. case 'standardRebate':
  180. toStandardRebate(row)
  181. break
  182. }
  183. }
  184. const deleteAccountDialogVisible = ref(false)
  185. const openDeleteAccountDialogs = () => {
  186. deleteAccountDialogVisible.value = true
  187. }
  188. const createAccount = () => {
  189. getCustomLoginInfo()
  190. }
  191. const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
  192. watch(cativeIndex, (newVal) => {
  193. // search.value.platform = tabs.value[newVal].id
  194. getAccountList()
  195. })
  196. const loading = ref(false)
  197. // 获取客户登录信息
  198. const dialogCheck = ref(false)
  199. async function getCustomLoginInfo() {
  200. try {
  201. const res = await userApi.getUserInfo();
  202. userStore.saveUserInfo(res.data);
  203. if (res.code === 200) {
  204. if (
  205. res.data.customInfo.status == 2 &&
  206. res.data.customInfo.applyRealStatus == 2
  207. ) {
  208. router.push(`/pages/customer/account-select?server=${cativeIndex.value == 1 ? 'demo' : 'real'}`)
  209. } else {
  210. dialogCheck.value = true;
  211. }
  212. }
  213. } catch (error) {
  214. // console.log(error, 111);
  215. }
  216. }
  217. const confirm = () => {
  218. dialogCheck.value = false;
  219. router.push(`/pages/mine/improveImmediately`)
  220. }
  221. const AccountList = ref([])
  222. const getAccountList = async () => {
  223. AccountList.value = []
  224. loading.value = true
  225. const api = cativeIndex.value == 1 ? customApi.demoList : customApi.AccountAllList
  226. const res = await api({
  227. page: {
  228. current: 1,
  229. size: 100
  230. }
  231. })
  232. if (res.code === 200) {
  233. AccountList.value = res.data
  234. }
  235. loading.value = false
  236. }
  237. // 格式化数值函数
  238. function formatMoney(value) {
  239. if (value === null || value === undefined) value = 0;
  240. const sign = value >= 0 ? '' : '-';
  241. const absoluteValue = Math.abs(value);
  242. return '$' + sign + absoluteValue.toFixed(2);
  243. }
  244. // 转换数组
  245. const accounts = computed(() =>
  246. AccountList.value && AccountList.value.length != 0 ? AccountList.value.map((acc, index) => {
  247. const currency = acc.currency || 'USD';
  248. const floating = acc.floating ?? 0;
  249. let labels = [t('vu.item1'), 'MT4', 'Standard'];
  250. labels[0] = cativeIndex.value == 1 ? t('vu.item2') : t('vu.item1');
  251. labels[1] = acc.platform || 'MT4';
  252. labels[2] = typeMap.value[acc.type];
  253. let nickname = typeMap.value[acc.type];
  254. let fwq
  255. if (cativeIndex.value != 1) {
  256. fwq = acc.platform == 'MT4' ? 'CWGMarketsLtd-Live' : 'CWGMarketsSVG-Live';
  257. } else {
  258. fwq = acc.platform == 'MT4' ? 'CWGMarketsLtd-Demo' : 'CWGMarketsSVG-Demo';
  259. }
  260. const balance = acc.balance
  261. return {
  262. ...acc,
  263. labels,
  264. isExpanded: index == 0,
  265. balance,
  266. accountNumber: acc.login.toString(),
  267. nickname,
  268. fwq,
  269. balanceWithSymbol: acc.balanceWithSymbol ?? '$0', // 余额
  270. creditWithSymbol: acc.creditWithSymbol ?? '$0',//信用
  271. equityWithSymbol: acc.equityWithSymbol ?? '$0',//净值
  272. currency,
  273. actualLeverage: '1:' + (acc.leverage ?? 0), // 实际杠杆
  274. floatingPL: formatMoney(floating), // 盈亏
  275. platform: acc.platform || 'MT4',
  276. server: acc.groupCode || '',
  277. login: acc.login.toString(),
  278. listType: cativeIndex.value == 1 ? 'demo' : 'real'
  279. };
  280. }) : []
  281. )
  282. onMounted(async () => {
  283. await getAccountList()
  284. })
  285. </script>
  286. <style scoped lang="scss">
  287. @import "@/uni.scss";
  288. .btn {
  289. margin: 0;
  290. }
  291. </style>