AccountCardMobile.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <template>
  2. <view class="account-card">
  3. <!-- 更多按钮 -->
  4. <!-- 标签区域 -->
  5. <view class="card-top">
  6. <view class="labels-container">
  7. <view class="account-number">{{ account.accountNumber }}</view>
  8. <view class="account-number" @click="copy(account.fwq)">{{ account.fwq }}</view>
  9. <view class="labels">
  10. <template v-for="(label, index) in account.labels" :key="index">
  11. <view v-if="label" class="label-badge">
  12. {{ label }}
  13. </view>
  14. </template>
  15. </view>
  16. </view>
  17. <cwg-dropdown ref="dropdownRef" @open="onOpen" @close="onClose" :menu-list="customMenuList"
  18. @menuClick="handleCustomClick">
  19. <view class="more-icon more-btn">
  20. <cwg-icon name="crm-ellipsis-vertical" :size="20" color="#6c8595" />
  21. </view>
  22. <template #btn>
  23. <view class="mobile-buttons">
  24. <!-- 交易 -->
  25. <template v-for="(item, index) in circleButtons" :key="index">
  26. <view class="circle-btn" :class="{ 'is-disabled': item.disabled }"
  27. @click="handleAction1(item)" v-if="!item.needDemo">
  28. <view class="circle-icon" :class="{ 'primary': item.primary }">
  29. <cwg-icon :name="item.icon" :size="16" :color="item.color" />
  30. </view>
  31. <text class="circle-label" v-t="item.label" />
  32. </view>
  33. </template>
  34. </view>
  35. </template>
  36. </cwg-dropdown>
  37. </view>
  38. <!-- 主要内容区域(余额和操作按钮) -->
  39. <view class="main-content">
  40. <!-- 余额 -->
  41. <view class="balance">
  42. <text class="balance-number">{{ balanceInteger }}</text>
  43. <text class="balance-decimal">{{ balanceDecimal }} {{ account.currency }}</text>
  44. </view>
  45. <!-- 移动端圆形按钮组 -->
  46. </view>
  47. <view class="info-bottom">
  48. <!-- 底部信息区域(折叠时隐藏) -->
  49. <view class="info-section">
  50. <view class="info-column">
  51. <cwg-label-line-value :label="t('Label.Leverage')" :value="account.actualLeverage" />
  52. <cwg-label-line-value :label="t('Label.FloatingPL')" :value="account.floatingPL" />
  53. <cwg-label-line-value :label="t('Label.Balance')" :value="account.balanceWithSymbol" />
  54. </view>
  55. <view class="info-column">
  56. <cwg-label-line-value :label="t('Label.Equity')" :value="account.equityWithSymbol" />
  57. <cwg-label-line-value :label="t('Label.Credit')" :value="account.creditWithSymbol" />
  58. <cwg-label-line-value :label="t('Documentary.console.item3')" :value="account.platform" />
  59. </view>
  60. </view>
  61. </view>
  62. <TerminalDialog v-model:visible="terminalDialogVisible" />
  63. <TerminalChangePasswordDialog v-model:visible="terminalChangePasswordDialogVisible" :pwdType="pwdType"
  64. :account="account" :accountLabel="t('Documentary.tradingCenter.item29') + ' # '" />
  65. <TerminalInfoDialog v-model:visible="terminalInfoDialogVisible" :accountNumber="accountInfo.login"
  66. :form="accountInfo" :fieldList="fieldList" :title="t('Documentary.TundManagement.item29')"
  67. :accountLabel="t('Documentary.tradingCenter.item29') + ' # '" />
  68. </view>
  69. </template>
  70. <script setup lang="ts">
  71. import { ref, computed, onMounted, nextTick, onBeforeUnmount } from 'vue';
  72. import useRouter from "@/hooks/useRouter";
  73. const router = useRouter();
  74. import { useI18n } from 'vue-i18n';
  75. const { t } = useI18n();
  76. import TerminalDialog from './TerminalDialog.vue'
  77. import TerminalChangePasswordDialog from './TerminalChangePasswordDialog.vue'
  78. import TerminalInfoDialog from './TerminalInfoDialog.vue'
  79. const props = defineProps<{
  80. account: Account;
  81. }>();
  82. const accountInfo = ref(props.account)
  83. // 定义账户数据类型
  84. export interface Account {
  85. labels: string[]; // 标签数组,如 ['真实', 'MT4', 'Standard']
  86. accountNumber: string; // 账号,如 '85319215'
  87. nickName: string; // 昵称,如 '标准账户'
  88. balance: number; // 余额数字
  89. currency: string; // 货币,如 'USD'
  90. actualLeverage: string; // 实际杠杆,如 '1:2000'
  91. maxLeverage: string; // 调整杠杆,如 '1:2000'
  92. floatingPL: string; // 浮动盈亏,如 '0.00 USD'
  93. creditWithSymbol: string; // 可用保证金,如 '0.00 USD'
  94. equityWithSymbol: string; // 净值,如 '0.00 USD'
  95. platform: string; // 平台,如 'MT4'
  96. server: string; // 服务器,如 'Exness-Real28'
  97. login: string; // 登录名,如 '85319215'
  98. balanceWithSymbol: string; // 余额,如 '85319215'
  99. fwq: string; // 服务器名称
  100. listType: string; // 账户类型,如 'real' 或 'demo'
  101. closeFunctions?: string; // 关闭的功能
  102. }
  103. const isDemo = computed(() => accountInfo.value.listType == 'demo')
  104. const closeFunctionOpen = (code) => {
  105. const closeFunctions = accountInfo.value.closeFunctions || ""
  106. if (closeFunctions == null || closeFunctions === "") {
  107. return true;
  108. }
  109. return String(closeFunctions).indexOf(String(code)) === -1;
  110. }
  111. // 圆形按钮数据
  112. const circleButtons = ref([
  113. { key: 'trade', label: 'Shop.Index.Transaction', icon: 'crm-trade', action: 'trade', needDemo: false, primary: true, disabled: false, color: '#fff' },
  114. { key: 'deposit', label: 'Home.page_customer.item2', icon: 'crm-deposit', action: 'deposit', needDemo: isDemo.value, disabled: !closeFunctionOpen('1'), color: '#2e3a47' },
  115. { key: 'withdraw', label: 'Home.page_customer.item3', icon: 'crm-withdraw', action: 'withdraw', needDemo: isDemo.value, disabled: !closeFunctionOpen('2'), color: '#2e3a47' },
  116. { key: 'transfer', label: 'Custom.Index.Transfer', icon: 'crm-transfer', action: 'transfer', needDemo: isDemo.value, disabled: !(closeFunctionOpen('5') && closeFunctionOpen('6') && closeFunctionOpen('3')), color: '#2e3a47' }
  117. ])
  118. const fieldList = ref([
  119. { label: t('Custom.PaymentHistory.AccountType'), key: 'nickname', copyable: false },
  120. { label: t('Label.Leverage'), key: 'actualLeverage', copyable: false },
  121. { label: t('Label.FloatingPL'), key: 'floatingPL', copyable: false },
  122. { label: t('Label.Balance'), key: 'balanceWithSymbol', copyable: false },
  123. { label: t('Label.Equity'), key: 'equityWithSymbol', copyable: false },
  124. { label: t('Label.Credit'), key: 'creditWithSymbol', copyable: false },
  125. { label: t('Documentary.console.item3'), key: 'platform', copyable: false },
  126. { label: t('Documentary.console.item4'), key: 'login', copyable: true }
  127. ])
  128. const nickName = ref(accountInfo.value.nickName);
  129. const terminalDialogVisible = ref(false)
  130. const terminalChangePasswordDialogVisible = ref(false)
  131. const terminalInfoDialogVisible = ref(false)
  132. const pwdType = ref(1)
  133. const handleAction1 = (item) => {
  134. if (item.disabled) {
  135. uni.showToast({
  136. title: t('news_add_field.Des.item1'),
  137. icon: 'none'
  138. })
  139. return
  140. }
  141. handleAction(item.action, item)
  142. }
  143. const dropdownRef = ref(null)
  144. // 处理按钮操作
  145. const handleAction = (type: string) => {
  146. dropdownRef.value.close()
  147. switch (type) {
  148. case 'trade':
  149. terminalDialogVisible.value = true
  150. break;
  151. case 'changePassword1':
  152. pwdType.value = 1
  153. terminalChangePasswordDialogVisible.value = true
  154. break;
  155. case 'changePassword2':
  156. pwdType.value = 2
  157. terminalChangePasswordDialogVisible.value = true
  158. break;
  159. case 'info':
  160. terminalInfoDialogVisible.value = true
  161. break;
  162. case 'deposit':
  163. toDeposit()
  164. break;
  165. case 'withdraw':
  166. toWithdraw()
  167. break;
  168. case 'transfer':
  169. toTransfer()
  170. break;
  171. case 'position':
  172. toPosition()
  173. break;
  174. case 'history':
  175. toHistory()
  176. break;
  177. case 'payment-history':
  178. toPaymentHistory()
  179. break;
  180. default:
  181. break;
  182. }
  183. };
  184. const customMenuList = computed(() => !isDemo.value ? [
  185. { label: t('Ib.Report.Tit1'), type: 'history' },
  186. { label: t('Ib.Report.Tit4'), type: 'position' },
  187. { label: t('Home.page_customer.item4'), type: 'payment-history' },
  188. { label: t('Documentary.TundManagement.item29'), type: 'info' },
  189. { label: t('vu.item3'), type: 'changePassword1' },
  190. { label: t('vu.item4'), type: 'changePassword2' }
  191. ] : [
  192. { label: t('Ib.Report.Tit1'), type: 'history' },
  193. { label: t('Ib.Report.Tit4'), type: 'position' },
  194. { label: t('Documentary.TundManagement.item29'), type: 'info' }
  195. ])
  196. const handleCustomClick = (item, index) => {
  197. handleAction(item.value.type)
  198. }
  199. // 复制文本
  200. const copy = (text: string) => {
  201. uni.setClipboardData({ data: text });
  202. };
  203. // 按钮对应的操作方法
  204. const toHistory = () => {
  205. router.push(`/pages/customer/trade-history?login=${accountInfo.value.login}`)
  206. }
  207. // 按钮对应的操作方法
  208. const toPosition = () => {
  209. router.push(`/pages/customer/trade-position?login=${accountInfo.value.login}`)
  210. }
  211. // 按钮对应的操作方法
  212. const toPaymentHistory = () => {
  213. router.push(`/pages/customer/payment-history?login=${accountInfo.value.login}`)
  214. }
  215. // 按钮对应的操作方法
  216. const toDeposit = () => {
  217. router.push(`/pages/customer/deposit?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
  218. }
  219. const toWithdraw = () => {
  220. router.push(`/pages/customer/withdrawal?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
  221. }
  222. const toTransfer = () => {
  223. router.push(`/pages/customer/transfer?login=${accountInfo.value.login}&type=${accountInfo.value.type}&balance=${accountInfo.value.balance}&currency=${accountInfo.value.currency}`)
  224. }
  225. // 格式化余额,拆分为整数和小数部分
  226. const balanceInteger = computed(() => {
  227. return Math.floor(accountInfo.value.balance).toString();
  228. });
  229. const balanceDecimal = computed(() => {
  230. const parts = accountInfo.value.balance.toFixed(2).split('.');
  231. return parts[1] ? '.' + parts[1] : '.00';
  232. });
  233. // 在组件挂载后初始化
  234. onMounted(() => {
  235. });
  236. onBeforeUnmount(() => {
  237. });
  238. </script>
  239. <style scoped lang="scss">
  240. @import '@/uni.scss';
  241. .account-card {
  242. border-radius: px2rpx(16);
  243. padding: px2rpx(16);
  244. margin-bottom: px2rpx(16);
  245. position: relative;
  246. border: 1px solid rgba(108, 133, 149, 0.12);
  247. color: #2e3a47;
  248. .labels-container {
  249. display: flex;
  250. align-items: center;
  251. flex-wrap: wrap;
  252. gap: px2rpx(12);
  253. margin-bottom: px2rpx(16);
  254. .labels {
  255. display: flex;
  256. gap: px2rpx(8);
  257. }
  258. .label-badge {
  259. background-color: rgba(108, 133, 149, 0.08);
  260. border-radius: px2rpx(4);
  261. padding: px2rpx(4) px2rpx(8);
  262. font-size: px2rpx(12);
  263. color: #6c8595;
  264. font-weight: 500;
  265. }
  266. .account-number {
  267. font-size: px2rpx(18);
  268. font-weight: 600;
  269. line-height: 1.3;
  270. }
  271. .account-nickname {
  272. font-size: px2rpx(14);
  273. color: #6c8595;
  274. }
  275. }
  276. .main-content {
  277. .balance {
  278. margin-bottom: px2rpx(16);
  279. .balance-number {
  280. font-size: px2rpx(32);
  281. font-weight: 700;
  282. }
  283. .balance-decimal {
  284. font-size: px2rpx(16);
  285. color: #6c8595;
  286. margin-left: px2rpx(4);
  287. }
  288. }
  289. }
  290. .info-section {
  291. display: flex;
  292. flex-direction: column;
  293. gap: px2rpx(12);
  294. padding: px2rpx(16) 0;
  295. margin: px2rpx(16) 0;
  296. .info-column {
  297. flex: 1;
  298. display: flex;
  299. flex-direction: column;
  300. gap: px2rpx(12);
  301. }
  302. .info-item {
  303. display: flex;
  304. justify-content: space-between;
  305. align-items: flex-end;
  306. font-size: px2rpx(14);
  307. .label {
  308. color: #6c8595;
  309. }
  310. .line {
  311. flex: 1;
  312. height: 1px;
  313. border-top: 1px dashed rgba(108, 133, 149, 0.5);
  314. margin-bottom: px2rpx(1);
  315. }
  316. .value {
  317. font-weight: 500;
  318. color: #2e3a47;
  319. }
  320. }
  321. }
  322. @media screen and (max-width: 768px) {
  323. .info-section {
  324. flex-direction: column;
  325. gap: px2rpx(12);
  326. }
  327. }
  328. .extra-actions {
  329. display: flex;
  330. align-items: center;
  331. gap: px2rpx(8);
  332. margin-bottom: px2rpx(12);
  333. .copy-row {
  334. display: flex;
  335. align-items: center;
  336. gap: px2rpx(4);
  337. font-size: px2rpx(14);
  338. .label {
  339. color: #6c8595;
  340. min-width: px2rpx(30);
  341. }
  342. .value {
  343. flex: 1;
  344. color: #2e3a47;
  345. font-family: monospace;
  346. }
  347. .copy-btn {
  348. background: transparent;
  349. border: 1px solid rgba(108, 133, 149, 0);
  350. padding: 0;
  351. cursor: pointer;
  352. color: #6c8595;
  353. width: px2rpx(24);
  354. height: px2rpx(24);
  355. display: flex;
  356. align-items: center;
  357. justify-content: center;
  358. box-sizing: border-box;
  359. svg {
  360. width: px2rpx(20);
  361. height: px2rpx(20);
  362. }
  363. &:hover {
  364. background-color: rgba(108, 133, 149, 0.05);
  365. border: 1px solid rgba(108, 133, 149, 0.2);
  366. }
  367. }
  368. }
  369. .divider {
  370. width: 1px;
  371. height: px2rpx(20);
  372. border-left: 1px solid rgba(108, 133, 149, 0.5);
  373. }
  374. .change-password-btn {
  375. background: transparent;
  376. box-sizing: border-box;
  377. border: 1px solid rgba(108, 133, 149, 0);
  378. border-radius: px2rpx(8);
  379. padding: px2rpx(8) px2rpx(16);
  380. font-size: px2rpx(14);
  381. color: #2e3a47;
  382. display: inline-flex;
  383. gap: px2rpx(8);
  384. cursor: pointer;
  385. margin: 0;
  386. .btn-icon svg {
  387. width: px2rpx(16);
  388. height: px2rpx(16);
  389. }
  390. &:hover {
  391. background-color: rgba(108, 133, 149, 0.05);
  392. border: 1px solid rgba(108, 133, 149, 0.2);
  393. }
  394. }
  395. }
  396. .notificators {
  397. // 预留通知区域
  398. }
  399. }
  400. .card-top {
  401. display: flex;
  402. justify-content: space-between;
  403. align-items: flex-start;
  404. :deep(.cwg-dropdown-menu-container) {
  405. left: px2rpx(-190) !important;
  406. }
  407. .more-btn {
  408. flex-shrink: 0;
  409. width: px2rpx(40);
  410. height: px2rpx(40);
  411. display: flex;
  412. align-items: center;
  413. justify-content: center;
  414. gap: px2rpx(4);
  415. background-color: rgba(108, 133, 149, 0.08);
  416. border-radius: px2rpx(4);
  417. cursor: pointer;
  418. &:hover {
  419. background-color: rgba(108, 133, 149, 0.1);
  420. }
  421. }
  422. // 移动端按钮组
  423. .mobile-buttons {
  424. display: flex;
  425. justify-content: center;
  426. gap: px2rpx(12);
  427. padding-bottom: px2rpx(16);
  428. padding: px2rpx(20);
  429. border-bottom: 1px solid #f0f0f0;
  430. .circle-btn {
  431. display: flex;
  432. flex-direction: column;
  433. align-items: center;
  434. cursor: pointer;
  435. width: px2rpx(40);
  436. .circle-icon {
  437. width: px2rpx(40);
  438. height: px2rpx(40);
  439. border-radius: 50%;
  440. background-color: #f5f7f9;
  441. display: flex;
  442. align-items: center;
  443. justify-content: center;
  444. color: #2e3a47;
  445. transition: background-color 0.2s;
  446. &.primary {
  447. background-color: var(--color-navy-700);
  448. color: #fff;
  449. }
  450. &:hover {
  451. background-color: #e9ecef;
  452. }
  453. &.primary:hover {
  454. background-color: var(--color-navy-700);
  455. color: #fff;
  456. }
  457. svg {
  458. width: px2rpx(24);
  459. height: px2rpx(24);
  460. }
  461. }
  462. .circle-label {
  463. font-size: px2rpx(12);
  464. margin-top: px2rpx(4);
  465. color: #6c8595;
  466. }
  467. }
  468. }
  469. .is-disabled {
  470. cursor: not-allowed;
  471. opacity: 0.5;
  472. }
  473. }
  474. </style>