AccountList.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. <template>
  2. <view class="account-card">
  3. <view class="content-title">
  4. <view v-t="'Custom.Index.AccountList'"></view>
  5. <view class="content-title-btns">
  6. <view class="btn-primary btn-primary1" @click="createAccount()">
  7. <cwg-icon icon="crm-plus" :size="16" color="#fff" />
  8. <text v-t="'Custom.Index.AddAccount'" />
  9. </view>
  10. </view>
  11. </view>
  12. <view class="tabs-container">
  13. <view class="tabs-class">
  14. <cwg-tabs v-model:cativeIndex="cativeIndex" :tabs="tabs" />
  15. </view>
  16. <view class="btn-class">
  17. <view class="btn-primary" :class="{ 'btn-primary2': isGridLayout }"
  18. @click="isGridLayout || toggleLayout()">
  19. <cwg-icon icon="crm-card" :size="16" color="#141d2299" />
  20. </view>
  21. <view class="btn-primary" :class="{ 'btn-primary2': !isGridLayout }"
  22. @click="!isGridLayout || toggleLayout()">
  23. <cwg-icon icon="crm-list" :size="16" color="#141d2299" />
  24. </view>
  25. </view>
  26. </view>
  27. <view v-if="accounts.length" :class="{ 'grid-layout': isGridLayout }">
  28. <AccountCard v-for="acc in accounts" :zhtype="cativeIndex" :key="acc.accountNumber" :account="acc"
  29. :is-grid-layout="isGridLayout" @action="handleAction" @copy="handleCopy"
  30. @change-password="handleChangePassword" />
  31. </view>
  32. <cwg-empty-state v-else />
  33. <DeleteAccountDialogs ref="deleteAccountDialogRef" v-model:visible="deleteAccountDialogVisible" />
  34. </view>
  35. </template>
  36. <script setup lang="ts">
  37. import { computed, ref, onMounted, watch } from 'vue';
  38. import { useI18n } from 'vue-i18n';
  39. const { t, locale } = useI18n();
  40. import useRouter from "@/hooks/useRouter";
  41. const router = useRouter();
  42. import { customApi } from '@/service/custom';
  43. import { userApi } from '@/api/user';
  44. import useUserStore from "@/stores/use-user-store";
  45. const userStore = useUserStore();
  46. import AccountCard from './AccountCard.vue'
  47. import DeleteAccountDialogs from './DeleteAccountDialogs.vue'
  48. import { useFilters } from '@/composables/useFilters'
  49. const { numberFormat, numberDecimal } = useFilters()
  50. const search = ref({ platform: 'MT4' })
  51. const isAfterJuly28 = () => {
  52. const now = new Date();
  53. const july28 = new Date(2025, 6, 28, 0, 0, 0); // 月份从0开始,所以7月是6
  54. return now >= july28;
  55. }
  56. const handleAction = (type) => { /* 处理交易/入金等 */ };
  57. const handleCopy = (text) => { uni.setClipboardData({ data: text }); };
  58. const handleChangePassword = () => { /* 跳转修改密码 */ };
  59. const typeMap = computed(() => ({
  60. 1: t('AccountType.ClassicAccount'),
  61. 2: t('AccountType.SeniorAccount'),
  62. 3: !isAfterJuly28() ? t('AccountType.AgencyAccount') : '',
  63. 5: t('AccountType.SpeedAccount'),
  64. 6: t('AccountType.SpeedAccount'),
  65. 7: t('AccountType.StandardAccount'),
  66. 8: t('AccountType.CentAccount')
  67. }));
  68. const cativeIndex = ref(0)
  69. const isGridLayout = ref(true)
  70. const tabs = computed(() => ([
  71. { id: 'real', name: t('vu.item1') },
  72. { id: 'demo', name: t('vu.item2') }
  73. ]))
  74. const toggleLayout = () => {
  75. isGridLayout.value = !isGridLayout.value
  76. }
  77. const tableRef = ref(null)
  78. const expanded = ref(null)
  79. const toggleRowExpand = (row) => {
  80. expanded.value = row.expanded ? row.rowIndex : null
  81. }
  82. const toggleExpand = (index) => {
  83. tableRef?.value.toggleRowExpand(index)
  84. }
  85. const createActionButtons = (row) => {
  86. console.log(row, 'row');
  87. return computed(() => [
  88. {
  89. icon: 'crm-circle-dollar-to-slot',
  90. text: t('Custom.Index.Deposit'),
  91. action: 'deposit',
  92. disabled: row.closeFunctions?.indexOf('1') !== -1,
  93. show: true,
  94. class: 'deposit-btn'
  95. },
  96. {
  97. icon: 'crm-credit-card',
  98. text: t('Custom.Index.Withdrawals'),
  99. action: 'withdraw',
  100. disabled: row.closeFunctions?.indexOf('2') !== -1,
  101. show: true,
  102. class: 'withdraw-btn'
  103. },
  104. {
  105. icon: 'crm-money-bill-transfer',
  106. text: t('Custom.Index.Transfer'),
  107. action: 'transfer',
  108. disabled: (
  109. row.closeFunctions?.indexOf('5') !== -1 ||
  110. row.closeFunctions?.indexOf('6') !== -1 ||
  111. row.closeFunctions?.indexOf('3') !== -1
  112. ),
  113. show: true,
  114. class: 'transfer-btn'
  115. },
  116. {
  117. icon: 'crm-gear',
  118. text: t('Custom.Index.Settings'),
  119. action: 'settings',
  120. disabled: row.closeFunctions?.indexOf('4') !== -1,
  121. show: true,
  122. class: 'settings-btn'
  123. },
  124. {
  125. icon: 'crm-award',
  126. text: t('standardRebate.item1'),
  127. action: 'standardRebate',
  128. disabled: false,
  129. show: row.type === 7,
  130. class: 'rebate-btn'
  131. }
  132. ])
  133. }
  134. // 处理按钮点击
  135. const handleActionBtn = (action, row) => {
  136. switch (action) {
  137. case 'deposit':
  138. toDeposit(row)
  139. break
  140. case 'withdraw':
  141. toWithdraw(row)
  142. break
  143. case 'transfer':
  144. toTransfer(row)
  145. break
  146. case 'settings':
  147. toSettings(row)
  148. break
  149. case 'standardRebate':
  150. toStandardRebate(row)
  151. break
  152. }
  153. }
  154. const deleteAccountDialogVisible = ref(false)
  155. const openDeleteAccountDialogs = () => {
  156. deleteAccountDialogVisible.value = true
  157. }
  158. const createAccount = () => {
  159. getCustomLoginInfo()
  160. }
  161. const isZh = computed(() => ['cn', 'zh', 'zhHant'].includes(locale.value));
  162. watch(cativeIndex, (newVal) => {
  163. // search.value.platform = tabs.value[newVal].id
  164. getAccountList()
  165. })
  166. // 获取客户登录信息
  167. async function getCustomLoginInfo() {
  168. try {
  169. const res = await userApi.getUserInfo();
  170. userStore.saveUserInfo(res.data);
  171. if (res.code === 200) {
  172. if (
  173. res.data.customInfo.status == 2 &&
  174. res.data.customInfo.applyRealStatus == 2
  175. ) {
  176. router.push(`/pages/customer/account-select?server=${cativeIndex.value == 1 ? 'demo' : 'real'}`)
  177. } else {
  178. // this.dialogCheck = true;
  179. }
  180. }
  181. } catch (error) {
  182. // console.log(error, 111);
  183. }
  184. }
  185. const AccountList = ref([])
  186. const getAccountList = async () => {
  187. AccountList.value = []
  188. const api = cativeIndex.value == 1 ? customApi.demoList : customApi.AccountAllList
  189. const res = await api({
  190. page: {
  191. current: 1,
  192. size: 100
  193. }
  194. })
  195. if (res.code === 200) {
  196. AccountList.value = res.data
  197. }
  198. }
  199. // 格式化数值函数
  200. function formatMoney(value) {
  201. if (value === null || value === undefined) value = 0;
  202. const sign = value >= 0 ? '' : '-';
  203. const absoluteValue = Math.abs(value);
  204. return sign + '$' + absoluteValue.toFixed(2);
  205. }
  206. // 转换数组
  207. const accounts = computed(() =>
  208. AccountList.value && AccountList.value.length != 0 ? AccountList.value.map((acc, index) => {
  209. const currency = acc.currency || 'USD';
  210. const floating = acc.floating ?? 0;
  211. let labels = [t('vu.item1'), 'MT4', 'Standard'];
  212. labels[0] = cativeIndex.value == 1 ? t('vu.item2') : t('vu.item1');
  213. labels[1] = acc.platform || 'MT4';
  214. labels[2] = typeMap.value[acc.type];
  215. let nickname = typeMap.value[acc.type];
  216. let fwq = acc.platform == 'MT4' ? 'MT4 CWGMarketsLtd-Live' : 'MT5 CWGMarketsSVG-Live';
  217. const balance = acc.balance
  218. return {
  219. ...acc,
  220. labels,
  221. isExpanded: index == 0,
  222. balance,
  223. accountNumber: acc.login.toString(),
  224. nickname,
  225. fwq,
  226. balanceWithSymbol: acc.balanceWithSymbol ?? '$0', // 余额
  227. creditWithSymbol: acc.creditWithSymbol ?? '$0',//信用
  228. equityWithSymbol: acc.equityWithSymbol ?? '$0',//净值
  229. currency,
  230. actualLeverage: '1:' + (acc.leverage ?? 0), // 实际杠杆
  231. floatingPL: formatMoney(floating), // 盈亏
  232. platform: acc.platform || 'MT4',
  233. server: acc.groupCode || '',
  234. login: acc.login.toString(),
  235. listType: cativeIndex.value == 1 ? 'demo' : 'real'
  236. };
  237. }) : []
  238. )
  239. onMounted(async () => {
  240. await getAccountList()
  241. })
  242. </script>
  243. <style scoped lang="scss">
  244. @import "@/uni.scss";
  245. .account-card {
  246. width: 100%;
  247. background-color: var(--color-white);
  248. box-sizing: border-box;
  249. }
  250. .tabs-container {
  251. display: flex;
  252. justify-content: space-between;
  253. align-items: center;
  254. gap: px2rpx(12);
  255. .tabs-class {
  256. width: px2rpx(200);
  257. margin: px2rpx(20) 0;
  258. }
  259. .btn-primary {
  260. width: px2rpx(26);
  261. height: px2rpx(26);
  262. border-radius: px2rpx(2);
  263. border: none;
  264. font-size: px2rpx(14);
  265. text-align: center;
  266. cursor: pointer;
  267. display: flex;
  268. align-items: center;
  269. justify-content: center;
  270. gap: px2rpx(8);
  271. }
  272. .btn-primary2 {
  273. background-color: rgba(108, 133, 149, 0.08);
  274. }
  275. .btn-class {
  276. display: flex;
  277. justify-content: center;
  278. align-items: center;
  279. border: 1px solid #f3f4f6;
  280. font-size: px2rpx(14);
  281. height: px2rpx(34);
  282. text-align: center;
  283. cursor: pointer;
  284. display: flex;
  285. align-items: center;
  286. justify-content: center;
  287. gap: px2rpx(8);
  288. padding: px2rpx(4) px2rpx(8);
  289. box-sizing: border-box;
  290. }
  291. .btn-primary:active {}
  292. }
  293. .content-title {
  294. display: flex;
  295. justify-content: space-between;
  296. align-items: center;
  297. font-size: px2rpx(22);
  298. font-weight: 500;
  299. color: var(--color-slate-800);
  300. background-color: rgba(255, 255, 255, 0);
  301. .content-title-btns {
  302. display: flex;
  303. align-items: center;
  304. justify-content: center;
  305. gap: px2rpx(12);
  306. .btn-primary {
  307. min-width: px2rpx(120);
  308. background-color: var(--color-error);
  309. color: var(--color-slate-150);
  310. padding: 0 px2rpx(12);
  311. border: none;
  312. font-size: px2rpx(14);
  313. text-align: center;
  314. cursor: pointer;
  315. display: flex;
  316. align-items: center;
  317. justify-content: center;
  318. gap: px2rpx(8);
  319. }
  320. .btn-primary1 {
  321. background-color: var(--color-navy-700);
  322. }
  323. .btn-primary2 {
  324. background-color: var(--color-secondary-focus);
  325. }
  326. }
  327. }
  328. /* 网格布局样式 */
  329. .grid-layout {
  330. display: grid;
  331. grid-template-columns: repeat(auto-fill, minmax(25%, 1fr));
  332. gap: px2rpx(20);
  333. @media (max-width: 1200px) {
  334. grid-template-columns: repeat(auto-fill, minmax(33.33%, 1fr));
  335. }
  336. @media (max-width: 768px) {
  337. grid-template-columns: repeat(auto-fill, minmax(100%, 1fr));
  338. }
  339. .account-card {
  340. margin-bottom: 0;
  341. height: 100%;
  342. display: flex;
  343. flex-direction: column;
  344. }
  345. .account-card .main-content {
  346. flex: 1;
  347. }
  348. }
  349. .operation-btn {
  350. :deep(span) {
  351. display: flex;
  352. align-items: center;
  353. justify-content: center;
  354. gap: px2rpx(4);
  355. cursor: pointer;
  356. background-color: var(--color-slate-150);
  357. padding: px2rpx(8) 0;
  358. }
  359. }
  360. .operation-btn.disabled {
  361. cursor: not-allowed;
  362. opacity: 0.5;
  363. }
  364. .search-bar {
  365. display: flex;
  366. align-items: center;
  367. justify-content: flex-start;
  368. flex-wrap: wrap;
  369. gap: px2rpx(16);
  370. margin: px2rpx(16) 0;
  371. .cwg-combox,
  372. .uni-easyinput,
  373. .uni-date {
  374. width: px2rpx(240) !important;
  375. flex: none;
  376. }
  377. }
  378. .expand-btn {
  379. display: flex;
  380. align-items: center;
  381. justify-self: center;
  382. gap: 4px;
  383. cursor: pointer;
  384. width: px2rpx(32);
  385. height: px2rpx(32);
  386. justify-content: center;
  387. .icon {
  388. transition: transform 0.3s ease;
  389. }
  390. &:hover {
  391. background-color:
  392. color-mix(in oklab, var(--color-slate-300) 20%, transparent);
  393. }
  394. &.expanded {
  395. background-color:
  396. color-mix(in oklab, var(--color-slate-300) 20%, transparent);
  397. }
  398. &.expanded .icon {
  399. transform: rotate(180deg);
  400. }
  401. }
  402. .action-buttons {
  403. display: flex;
  404. flex-wrap: wrap;
  405. gap: px2rpx(8);
  406. justify-content: flex-end;
  407. padding: px2rpx(16) px2rpx(20);
  408. background-color: var(--color-slate-100);
  409. .action-btn {
  410. display: inline-flex;
  411. align-items: center;
  412. height: px2rpx(24);
  413. padding: 0 px2rpx(16);
  414. border: none;
  415. background-color: var(--color-secondary-focus);
  416. color: var(--color-white);
  417. font-size: px2rpx(13);
  418. font-weight: 500;
  419. cursor: pointer;
  420. transition: all 0.2s ease;
  421. .icon {
  422. margin-right: px2rpx(4);
  423. }
  424. }
  425. .action-btn.disabled {
  426. cursor: not-allowed;
  427. opacity: 0.5;
  428. }
  429. }
  430. </style>