AccountList.vue 14 KB

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