AccountCardMobile.vue 17 KB

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