dashboard.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107
  1. <template>
  2. <cwg-page-wrapper class="create-page" :isHeaderFixed="true">
  3. <view class="header">
  4. <view class="title">
  5. <view class="mb-3">{{ t('Home.msg.Custom') }}</view>
  6. <view class="">{{ dateWeek }}</view>
  7. </view>
  8. <button type="button" class="btn btn-secondary btn-shadow waves-effect"
  9. @click="createAccount">
  10. <view class="d-flex align-items-center">
  11. <cwg-icon name="crm-plus" :size="14" color="#fff" />
  12. <text v-t="'Custom.Index.AddAccount'" />
  13. </view>
  14. </button>
  15. </view>
  16. <uni-loading v-if="loading" />
  17. <uni-row v-else class="demo-uni-row uni-row1" :gutter="20">
  18. <uni-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  19. <uni-row :gutter="10">
  20. <uni-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
  21. <view class="card position-relative">
  22. <view class="card-body d-flex align-items-center gap-3">
  23. <view class="icon-placeholder">
  24. <cwg-icon name="crm-wallet" :size="28" color="red" />
  25. </view>
  26. <view class="flex-1">
  27. <view class="d-flex justify-between items-start mb-1">
  28. <text class="text-muted text-sm">{{t('vu.custom.t1')}}</text>
  29. <text
  30. :class="['growth-rate', compareData.totalBalanceGrowthRate||0 >= 0 ? 'rate-up' : 'rate-down']">
  31. {{ compareData.totalBalanceGrowthRate || 0 >= 0 ? '+' : ''
  32. }}{{ compareData.totalBalanceGrowthRate || 0 }}%
  33. </text>
  34. </view>
  35. <view class="fw-bold text-lg">${{ numberFormat(compareData.totalBalance || '0') }}</view>
  36. </view>
  37. </view>
  38. </view>
  39. </uni-col>
  40. <uni-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
  41. <view class="card position-relative">
  42. <view class="card-body d-flex align-items-center gap-3">
  43. <view class="icon-placeholder">
  44. <cwg-icon name="crm-money-up" :size="28" color="red" />
  45. </view>
  46. <view class="flex-1">
  47. <view class="d-flex justify-between items-start mb-1">
  48. <text class="text-muted text-sm">{{t('vu.custom.t2')}}</text>
  49. <text :class="['growth-rate', compareData.totalEquityGrowthRate ||0>= 0 ? 'rate-up' : 'rate-down']">
  50. {{ compareData.totalEquityGrowthRate || 0 >= 0 ? '+' : ''
  51. }}{{ compareData.totalEquityGrowthRate || 0 }}%
  52. </text>
  53. </view>
  54. <view class="fw-bold text-lg">${{ numberFormat(compareData.totalEquity || '0') }}</view>
  55. </view>
  56. </view>
  57. </view>
  58. </uni-col>
  59. <uni-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
  60. <view class="card position-relative">
  61. <view class="card-body d-flex align-items-center gap-3">
  62. <view class="icon-placeholder">
  63. <cwg-icon name="icon_withdraw" :size="28" color="red" />
  64. </view>
  65. <view class="flex-1">
  66. <view class="d-flex justify-between items-start mb-1">
  67. <text class="text-muted text-sm">{{t('vu.custom.t3')}}</text>
  68. <text
  69. :class="['growth-rate', compareData.totalDepositAmountGrowthRate ||0 >= 0 ? 'rate-up' : 'rate-down']">
  70. {{ compareData.totalDepositAmountGrowthRate || 0 >= 0 ? '+' : ''
  71. }}{{ compareData.totalDepositAmountGrowthRate || 0 }}%
  72. </text>
  73. </view>
  74. <view class="fw-bold text-lg">${{ numberFormat(compareData.totalDepositAmount || '0') }}</view>
  75. </view>
  76. </view>
  77. </view>
  78. </uni-col>
  79. <uni-col :xs="24" :sm="12" :md="12" :lg="6" :xl="6">
  80. <view class="card position-relative">
  81. <view class="card-body d-flex align-items-center gap-3">
  82. <view class="icon-placeholder">
  83. <cwg-icon name="crm-money-bill-transfer" :size="28" color="red" />
  84. </view>
  85. <view class="flex-1">
  86. <view class="d-flex justify-between items-start mb-1">
  87. <text class="text-muted text-sm">{{t('vu.custom.t4')}}</text>
  88. <text
  89. :class="['growth-rate', compareData.totalWithdrawalAmountGrowthRate||0 >= 0 ? 'rate-up' : 'rate-down']">
  90. {{ compareData.totalWithdrawalAmountGrowthRate || 0 >= 0 ? '+' : ''
  91. }}{{ compareData.totalWithdrawalAmountGrowthRate || 0 }}%
  92. </text>
  93. </view>
  94. <view class="fw-bold text-lg">${{ numberFormat(compareData.totalWithdrawalAmount || '0') }}</view>
  95. </view>
  96. </view>
  97. </view>
  98. </uni-col>
  99. </uni-row>
  100. </uni-col>
  101. <uni-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  102. <uni-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
  103. <view class="chart-box crm-border-radius">
  104. <view class="chart-title">
  105. <uni-row>
  106. <uni-row>
  107. <view class="opt-title mb-2">
  108. <view class="bigtitle">
  109. <text v-if="chartForm.chartType == 4">{{ t('Custom.Index.TradingVolumeStatistics') }}</text>
  110. <text v-if="chartForm.chartType == 2">{{ t('Custom.Index.DepositStatistical') }}</text>
  111. <text v-if="chartForm.chartType == 3">{{ t('Custom.Index.WithdrawalsStatistical') }}</text>
  112. <text v-if="chartForm.chartType == 6">{{ t('Custom.Index.ProfitLoss') }}</text>
  113. </view>
  114. <view class="time" @click="toReload">
  115. <cwg-icon name="crm-refresh" :size="16" color="#666" />
  116. <text class="crm-cursor"> GMT+3 {{ time }}</text>
  117. </view>
  118. </view>
  119. </uni-row>
  120. <uni-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  121. <view class="btn-opt">
  122. <view class="title">
  123. <view class="account" v-if="chartForm.login">
  124. <text>{{ t('Custom.Index.TradingAccount') }}</text>
  125. <text> —</text>
  126. <text>{{ chartForm.login }}</text>
  127. </view>
  128. <view class="date">
  129. <uni-datetime-picker
  130. v-model="isDate"
  131. type="daterange"
  132. :placeholder="t('placeholder.Start') + ' - ' + t('placeholder.End')"
  133. @change="getSearchDate"
  134. />
  135. </view>
  136. </view>
  137. <button class="operation" @click="isChart = true">
  138. <text style="font-size: 14px">{{ t('Custom.Index.Parameter') }}</text>
  139. </button>
  140. </view>
  141. </uni-col>
  142. </uni-row>
  143. </view>
  144. <view class="chart-container">
  145. <cwg-match-media :max-width="991">
  146. <cwg-charts type="line" :chartData="chartData" :opts="chartOpts" tooltipFormat="tooltipCustom" ontouch />
  147. </cwg-match-media>
  148. <cwg-match-media :min-width="991">
  149. <cwg-charts type="line" :chartData="chartData" :opts="chartOpts" tooltipFormat="tooltipCustom" />
  150. </cwg-match-media>
  151. </view>
  152. </view>
  153. </uni-col>
  154. <uni-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12" style="height: 100%">
  155. <view class="chart-box mh crm-border-radius">
  156. <view class="bigtitle mb-3">
  157. {{t('Custom.Index.MyAccount')}}
  158. </view>
  159. <view class="mb-2">
  160. <cwg-combox :clearable="false" v-model:value="loginValue"
  161. :options="loginComboxOptions" :placeholder="t('placeholder.choose')" @change="setCurData"/>
  162. </view>
  163. <view>
  164. <AccountCard :key="curData?.accountNumber"
  165. :account="curData" noCtrl />
  166. </view>
  167. </view>
  168. </uni-col>
  169. </uni-col>
  170. </uni-row>
  171. <cwg-improve-popup v-model:visible="dialogCheck" @confirm="confirm" />
  172. <cwg-popup v-model:visible="isChart" type="center" :mask-click="false" :show-footers="true"
  173. :title="t('Custom.Index.ChartSet')">
  174. <view class="dia-content">
  175. <uni-forms ref="chartFormRef" labelWidth="160">
  176. <uni-forms-item :label="t('Label.ChartType') + ':'" prop="chartType">
  177. <cwg-combox
  178. v-model:value="chartForm.chartType"
  179. :options="chartTypeOptions"
  180. :placeholder="t('placeholder.choose')"
  181. />
  182. </uni-forms-item>
  183. <uni-forms-item :label="t('Label.PlatformType') + ':'" prop="platform">
  184. <cwg-combox
  185. v-model:value="chartForm.platform"
  186. :options="platformOptions"
  187. :placeholder="t('placeholder.choose')"
  188. @change="onPlatformChange"
  189. />
  190. </uni-forms-item>
  191. <uni-forms-item :label="t('Label.TradingAccount') + ':'" prop="login">
  192. <cwg-combox
  193. v-model:value="chartForm.login"
  194. :options="loginOptions"
  195. :placeholder="t('placeholder.choose')"
  196. />
  197. </uni-forms-item>
  198. </uni-forms>
  199. </view>
  200. <template #footer>
  201. <button @click="isChart = false">{{ t('Btn.Cancel') }}</button>
  202. <button type="primary" @click="handleChartConfirm">{{ t('Btn.Confirm') }}</button>
  203. </template>
  204. </cwg-popup>
  205. </cwg-page-wrapper>
  206. </template>
  207. <script setup>
  208. import { ref, computed, watch, onMounted, nextTick,onUnmounted } from 'vue'
  209. import { useI18n } from 'vue-i18n'
  210. import dayjs from 'dayjs'
  211. import useRouter from '@/hooks/useRouter'
  212. import config from '@/config/index'
  213. import useUserStore from '@/stores/use-user-store'
  214. import { useFilters } from '@/composables/useFilters'
  215. import { documentaryApi } from '@/service/documentary'
  216. import { customApi } from '@/service/custom'
  217. import { userApi } from '@/api/user'
  218. import AccountCard from '@/pages/customer/components/AccountCard.vue'
  219. const { t, locale } = useI18n()
  220. const loading = ref(false)
  221. const router = useRouter()
  222. const { Code } = config
  223. const userStore = useUserStore()
  224. const { numberFormat } = useFilters()
  225. const loginOptions = ref([])
  226. const ChartSetDate = ref({})
  227. const isDealLogin = ref(false)
  228. const dealDate = ref({})
  229. const SubscribeProfitDate = ref([])
  230. const compareData = ref({})
  231. const loginValue = ref('')
  232. const isChart = ref(false)
  233. const isDate = ref(['', ''])
  234. const time = ref('')
  235. const chartData = ref({ categories: [], series: [] })
  236. const chartOpts = ref({
  237. color: ['#1890FF', '#91CB74', '#FAC858', '#EE6666', '#73C0DE', '#3CA272', '#FC8452', '#9A60B4', '#ea7ccc'],
  238. padding: [15, 10, 0, 15],
  239. enableScroll: true,
  240. legend: {
  241. show: false,
  242. },
  243. xAxis: {
  244. // disableGrid: true,
  245. scrollShow: true,
  246. itemCount: 4,
  247. },
  248. tooltipFormat: 'tooltipCustom',
  249. extra: {
  250. line: {
  251. type: 'straight',
  252. width: 2,
  253. activeType: 'hollow',
  254. },
  255. tooltip: {
  256. legendShow: false,
  257. // showCategory: true,
  258. formatter: 'tooltipCustom',
  259. },
  260. },
  261. })
  262. const dropDown = ref([])
  263. const chartFormRef = ref(null)
  264. const chartForm = ref({
  265. chartType: 4,
  266. platform: '',
  267. login: '',
  268. })
  269. const chartTypeOptions = computed(() => [
  270. { text: t('Custom.Index.TradingVolumeStatistics'), value: 4 },
  271. { text: t('Custom.Index.DepositStatistical'), value: 2 },
  272. { text: t('Custom.Index.WithdrawalsStatistical'), value: 3 },
  273. { text: t('Custom.Index.ProfitLoss'), value: 6 },
  274. ])
  275. const platformOptions = [
  276. { text: 'MT4', value: 'MT4' },
  277. { text: 'MT5', value: 'MT5' },
  278. ]
  279. const loginDataOptions = ref([])
  280. const loginComboxOptions = computed(() => {
  281. return loginDataOptions.value.map((item) => ({
  282. text: item.label,
  283. value: item.login
  284. }))
  285. })
  286. const isAfterJuly28 = () => {
  287. const now = new Date();
  288. const july28 = new Date(2025, 6, 28, 0, 0, 0); // 月份从0开始,所以7月是6
  289. return now >= july28;
  290. }
  291. const getLocalTime = () => {
  292. let timezone = 2 //目标时区时间,东2区 东时区正数 西市区负数
  293. let offset_GMT = new Date().getTimezoneOffset() // 本地时间和格林威治的时间差,单位为分钟
  294. let nowDate = new Date().getTime() // 本地时间距 1970 年 1 月 1 日午夜(GMT 时间)之间的毫秒数
  295. let now = new Date(
  296. nowDate + offset_GMT * 60 * 1000 + timezone * 60 * 60 * 1000,
  297. )
  298. //当前时间
  299. let month = now.getMonth() + 1 //月
  300. let day = now.getDate() //日
  301. let hh = now.getHours() //时
  302. let mm = now.getMinutes() //分
  303. let clock
  304. if (month < 10) {
  305. month = '0' + month
  306. }
  307. if (day < 10) {
  308. day = '0' + day
  309. }
  310. if (hh < 10) {
  311. hh = '0' + hh
  312. }
  313. if (mm < 10) {
  314. mm = '0' + mm
  315. }
  316. clock = ' ' + hh + ':' + mm + ' ' + month + '/' + day
  317. time.value = clock
  318. }
  319. const toReload = () => {
  320. goTime()
  321. }
  322. // 获取账户信息
  323. const getDateList = async () => {
  324. loading.value = true
  325. let res = await customApi.CustomDropdown({ platform: "" })
  326. if (res.code == Code.StatusOK) {
  327. loginDataOptions.value = res.data.map(item => ({
  328. ...item,
  329. label: `${item.login}`
  330. }))
  331. const found = loginDataOptions.value?.[0]
  332. loginValue.value = found?.login||null
  333. } else {
  334. uni.showToast({ title: res.msg, icon: 'none' })
  335. }
  336. }
  337. const getSearchDate = (val) => {
  338. if (Array.isArray(val)) {
  339. isDate.value = val
  340. }
  341. fetchChartData()
  342. }
  343. const onPlatformChange = (val) => {
  344. console.log('Platform changed:', val)
  345. }
  346. const handleChartConfirm = async () => {
  347. try {
  348. const params = {
  349. type: chartForm.value.chartType || 4,
  350. platform: chartForm.value.platform,
  351. login: chartForm.value.login,
  352. startDate: isDate.value[0],
  353. endDate: isDate.value[1],
  354. }
  355. const res = await customApi.getChartInfo(params)
  356. if (res.code === Code.StatusOK) {
  357. if (res.data) {
  358. let categories = []
  359. let seriesData = []
  360. res.data.forEach((item) => {
  361. categories.push(item.date.split(' ')[0])
  362. seriesData.push(item.amount)
  363. })
  364. chartData.value = { categories, series: [{ name: '', data: seriesData }] }
  365. } else {
  366. chartData.value = { categories: [], series: [] }
  367. }
  368. }
  369. isChart.value = false
  370. } catch (error) {
  371. console.error('获取图表数据失败', error)
  372. isChart.value = false
  373. }
  374. }
  375. const fetchChartData = async () => {
  376. try {
  377. const params = {
  378. type: chartForm.value.chartType || 4,
  379. platform: chartForm.value.platform,
  380. login: chartForm.value.login,
  381. startDate: isDate.value[0],
  382. endDate: isDate.value[1],
  383. }
  384. const res = await customApi.getChartInfo(params)
  385. if (res.code === Code.StatusOK) {
  386. if (res.data) {
  387. let categories = []
  388. let seriesData = []
  389. res.data.forEach((item) => {
  390. categories.push(item.date.split(' ')[0])
  391. seriesData.push(item.amount)
  392. })
  393. chartData.value = { categories, series: [{ name: '', data: seriesData }] }
  394. } else {
  395. chartData.value = { categories: [], series: [] }
  396. }
  397. }
  398. } catch (error) {
  399. console.error('获取图表数据失败', error)
  400. }
  401. }
  402. const getDropDown = async () => {
  403. try {
  404. const res = await customApi.CustomDropdown()
  405. if (res.code === Code.StatusOK) {
  406. dropDown.value = res.data || []
  407. loginOptions.value = (res.data || []).map(item => ({
  408. text: item.login,
  409. value: item.login,
  410. }))
  411. }
  412. } catch (error) {
  413. console.error('获取账户列表失败', error)
  414. }
  415. }
  416. // 今日时间
  417. const dateWeek = computed(() => {
  418. const now = dayjs()
  419. const isChinese = locale.value === 'cn' || locale.value === 'zhHant'
  420. if (isChinese) {
  421. const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
  422. const months = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
  423. return `${weekDays[now.day()]}, ${months[now.month()]}${now.date()}日, ${now.year()}`
  424. } else {
  425. return now.format('ddd, MMMD, YYYY')
  426. }
  427. })
  428. const createAccount = () => {
  429. getCustomLoginInfo()
  430. }
  431. // 获取客户登录信息
  432. const dialogCheck = ref(false)
  433. async function getCustomLoginInfo() {
  434. try {
  435. const res = await userApi.getUserInfo()
  436. userStore.saveUserInfo(res.data)
  437. if (res.code === 200) {
  438. if (
  439. res.data.customInfo.status == 2 &&
  440. res.data.customInfo.applyRealStatus == 2
  441. ) {
  442. router.push(`/pages/customer/account-select?server=${'real'}`)
  443. } else {
  444. dialogCheck.value = true
  445. }
  446. }
  447. } catch (error) {
  448. // console.log(error, 111);
  449. }
  450. }
  451. const confirm = () => {
  452. dialogCheck.value = false
  453. router.push(`/pages/mine/improveImmediately`)
  454. }
  455. const getDailyCompare = async (login) => {
  456. const res = await documentaryApi.followDailyCompare({ login })
  457. if (res.code === Code.StatusOK) {
  458. ChartSetDate.value = res.data ?? {}
  459. } else {
  460. uni.showToast({ title: res.msg, icon: 'none' })
  461. }
  462. }
  463. const getDailyDeal = async (login) => {
  464. const res = await documentaryApi.followDailyDeal({ login })
  465. if (res.code === Code.StatusOK) {
  466. dealDate.value = res.data ?? {}
  467. } else {
  468. uni.showToast({ title: res.msg, icon: 'none' })
  469. }
  470. }
  471. const getDailySubscribeProfit = async (login) => {
  472. const res = await documentaryApi.followDailySubscribeProfit({ login })
  473. if (res.code === Code.StatusOK) {
  474. SubscribeProfitDate.value = res.data?.data ?? []
  475. } else {
  476. uni.showToast({ title: res.msg, icon: 'none' })
  477. }
  478. }
  479. watch(() => chartForm.value.login, async (login) => {
  480. await getDailyCompare(login)
  481. if (isDealLogin.value) {
  482. await getDailyDeal(login)
  483. } else {
  484. await getDailySubscribeProfit(login)
  485. }
  486. })
  487. const getData = async () => {
  488. try {
  489. const res = await customApi.customDailyCompare()
  490. if (res.code === Code.StatusOK) {
  491. compareData.value = res.data
  492. }
  493. } catch (e) {
  494. console.log(e)
  495. }
  496. }
  497. const goTime = () => {
  498. getLocalTime()
  499. let timezone = 2 //目标时区时间,东2区 东时区正数 西市区负数
  500. let offset_GMT = new Date().getTimezoneOffset() // 本地时间和格林威治的时间差,单位为分钟
  501. let nowDate = new Date().getTime() // 本地时间距 1970 年 1 月 1 日午夜(GMT 时间)之间的毫秒数
  502. let data = new Date(
  503. nowDate + offset_GMT * 60 * 1000 + timezone * 60 * 60 * 1000,
  504. )
  505. //当月第一天和最后一天
  506. let mon1 = data.setDate(1) //当月第一天
  507. let mon2 = data.setMonth(data.getMonth() + 1) //下月
  508. let y1 = new Date(mon1).getFullYear()
  509. let m1 = new Date(mon1).getMonth() + 1
  510. let d1 = new Date(mon1).getDate()
  511. let y2 = new Date(mon2).getFullYear()
  512. let m2 = new Date(mon2).getMonth() + 1
  513. let d2 = 1
  514. isDate.value = [
  515. y1 + '-' + (m1 < 10 ? '0' + m1 : m1) + '-' + (d1 < 10 ? '0' + d1 : d1),
  516. y2 + '-' + (m2 < 10 ? '0' + m2 : m2) + '-' + (d2 < 10 ? '0' + d2 : d2),
  517. ]
  518. fetchChartData()
  519. }
  520. const isMobile = ref(false)
  521. onUnmounted(() => {
  522. // #ifdef H5
  523. window.removeEventListener('resize', handleResize)
  524. // #endif
  525. })
  526. const chartShow = ref(true)
  527. // 监听窗口大小变化(仅 H5)
  528. // #ifdef H5
  529. const handleResize = () => {
  530. checkIsMobile()
  531. }
  532. // #endif
  533. const checkIsMobile = () => {
  534. // 适配 uni-app 环境
  535. chartShow.value = false
  536. const width = window.innerWidth
  537. console.log(width)
  538. isMobile.value = width < 991
  539. chartShow.value = true
  540. // #ifndef H5
  541. chartShow.value = false
  542. const systemInfo = uni.getSystemInfoSync()
  543. isMobile.value = systemInfo.windowWidth < 991
  544. chartShow.value = true
  545. // #endif
  546. }
  547. const AccountList = ref([])
  548. const getAccountList = async () => {
  549. AccountList.value = []
  550. const res = await customApi.AccountAllList({
  551. page: {
  552. current: 1,
  553. size: 100
  554. }
  555. })
  556. if (res.code === 200) {
  557. AccountList.value = res.data
  558. }
  559. }
  560. const typeMap = computed(() => ({
  561. 1: t('AccountType.ClassicAccount'),
  562. 2: t('AccountType.SeniorAccount'),
  563. 3: !isAfterJuly28() ? t('AccountType.AgencyAccount') : '',
  564. 5: t('AccountType.SpeedAccount'),
  565. 6: t('AccountType.SpeedAccount'),
  566. 7: t('AccountType.StandardAccount'),
  567. 8: t('AccountType.CentAccount')
  568. }));
  569. // 格式化数值函数
  570. function formatMoney(value) {
  571. if (value === null || value === undefined) value = 0;
  572. const sign = value >= 0 ? '' : '-';
  573. const absoluteValue = Math.abs(value);
  574. return '$' + sign + absoluteValue.toFixed(2);
  575. }
  576. // 转换数组
  577. const accounts = computed(() => {
  578. if (!AccountList.value || AccountList.value.length == 0) return []
  579. let filteredAccounts = AccountList.value
  580. return filteredAccounts.map((acc, index) => {
  581. const currency = acc.currency || 'USD';
  582. const floating = acc.floating ?? 0;
  583. let labels = [t('vu.item1'), 'MT4', 'Standard'];
  584. labels[0] = t('vu.item1');
  585. labels[1] = acc.platform || 'MT4';
  586. labels[2] = typeMap.value[acc.type];
  587. let nickname = typeMap.value[acc.type];
  588. let fwq
  589. fwq = acc.platform == 'MT4' ? 'CWGMarketsLtd-Live' : 'CWGMarketsSVG-Live';
  590. const balance = acc.balance
  591. return {
  592. ...acc,
  593. labels,
  594. isExpanded: index == 0,
  595. balance,
  596. accountNumber: acc.login.toString(),
  597. nickname,
  598. fwq,
  599. balanceWithSymbol: acc.balanceWithSymbol ?? '$0',
  600. creditWithSymbol: acc.creditWithSymbol ?? '$0',
  601. equityWithSymbol: acc.equityWithSymbol ?? '$0',
  602. currency,
  603. actualLeverage: '1:' + (acc.leverage ?? 0),
  604. floatingPL: formatMoney(floating),
  605. platform: acc.platform || 'MT4',
  606. server: acc.groupCode || '',
  607. login: acc.login.toString(),
  608. listType: 'real'
  609. };
  610. })
  611. })
  612. const curData = ref()
  613. const setCurData = (val)=>{
  614. console.log(accounts.value, val)
  615. curData.value = accounts.value.find(item=>item.login == val)
  616. }
  617. watch(() => accounts.value, (val)=>{
  618. if (val.length){
  619. curData.value = accounts.value.find(item=>item.login == loginValue.value)
  620. }
  621. })
  622. onMounted(async () => {
  623. loading.value = true
  624. goTime()
  625. await getData()
  626. await getDropDown()
  627. await getDateList()
  628. await getAccountList()
  629. // await fetchChartData()
  630. loading.value = false
  631. })
  632. </script>
  633. <style lang="scss" scoped>
  634. @import "@/uni.scss";
  635. .header {
  636. display: flex;
  637. align-items: center;
  638. justify-content: space-between;
  639. margin-bottom: px2rpx(20);
  640. .title {
  641. font-size: px2rpx(24);
  642. font-weight: bold;
  643. }
  644. }
  645. .demo-uni-row {
  646. display: flex;
  647. flex-wrap: wrap;
  648. align-items: stretch;
  649. margin: 0 auto !important;
  650. }
  651. .uni-col-left {
  652. //display: flex;
  653. //flex-direction: column;
  654. }
  655. .uni-col-right {
  656. display: flex;
  657. }
  658. .dashboard-container {
  659. min-height: 10vh;
  660. box-sizing: border-box;
  661. display: flex;
  662. flex-direction: column;
  663. height: 100%;
  664. }
  665. /* 卡片通用样式 */
  666. .card {
  667. background: var(--color-white);
  668. color: var(--bs-heading-color);
  669. border-radius: 4px;
  670. margin-bottom: px2rpx(20);
  671. box-shadow: 0 px2rpx(4) px2rpx(12) rgba(0, 0, 0, 0.2);
  672. }
  673. .card-body {
  674. padding: px2rpx(16);
  675. }
  676. .icon-placeholder {
  677. width: px2rpx(48);
  678. height: px2rpx(48);
  679. border-radius: px2rpx(24);
  680. background: linear-gradient(135deg, #f5f7fa 0%, #e4e8ec 100%);
  681. display: flex;
  682. align-items: center;
  683. justify-content: center;
  684. flex-shrink: 0;
  685. }
  686. .icon-text {
  687. font-size: px2rpx(20);
  688. font-weight: bold;
  689. color: #6c757d;
  690. }
  691. .growth-rate {
  692. font-size: px2rpx(12);
  693. font-weight: 500;
  694. padding: px2rpx(2) px2rpx(8);
  695. border-radius: px2rpx(2);
  696. }
  697. .growth-rate.rate-up {
  698. color: #10b981;
  699. background: rgba(16, 185, 129, 0.1);
  700. }
  701. .growth-rate.rate-down {
  702. color: #ef4444;
  703. background: rgba(239, 68, 68, 0.1);
  704. }
  705. .custom-number,
  706. .custom-money {
  707. background: var(--bs-body-bg);
  708. border: 1px solid var(--bs-border-color);
  709. padding: 15px;
  710. border-radius: 4px;
  711. margin-bottom: 20px;
  712. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  713. min-height: px2rpx(125);
  714. }
  715. .custom-number .title,
  716. .custom-money .left .tit,
  717. .custom-money .right .tit {
  718. font-size: 14px;
  719. color: var(--bs-heading-color);
  720. margin-bottom: 10px;
  721. }
  722. .custom-number .title {
  723. display: flex;
  724. justify-content: space-between;
  725. align-items: center;
  726. }
  727. .custom-money {
  728. display: flex;
  729. flex-direction: column;
  730. }
  731. .custom-money .left,
  732. .custom-money .right {
  733. flex: 1;
  734. }
  735. .custom-money .num {
  736. font-size: 20px;
  737. font-weight: bold;
  738. color: var(--bs-heading-color);
  739. }
  740. .custom-money .num.red {
  741. color: #eb3f57;
  742. }
  743. .el-dropdown-link {
  744. display: flex;
  745. align-items: center;
  746. gap: 4px;
  747. color: #6c8595;
  748. font-size: 12px;
  749. }
  750. .account-info1 {
  751. margin-bottom: 20px;
  752. }
  753. .custom-money-left .header,
  754. .custom-money-right .header {
  755. display: flex;
  756. align-items: center;
  757. justify-content: space-between;
  758. margin-bottom: px2rpx(15);
  759. }
  760. .custom-money-left .tab,
  761. .custom-money-right .tab {
  762. font-size: px2rpx(16);
  763. font-weight: 600;
  764. color: var(--bs-heading-color);
  765. }
  766. .bottomCol {
  767. font-size: 16px;
  768. color: #868686;
  769. .bo-left1 {
  770. margin: 20px 0;
  771. padding: 20px 0;
  772. border-right: 1px dashed #989898;
  773. text-align: center;
  774. font-size: 16px;
  775. }
  776. .bo-right1 {
  777. margin: 20px 0px;
  778. padding: 20px 0;
  779. text-align: center;
  780. font-size: 16px;
  781. }
  782. }
  783. .blue-font {
  784. margin-top: px2rpx(15);
  785. color: #007aff;
  786. font-weight: 600;
  787. font-size: px2rpx(24);
  788. }
  789. .subscribe-table {
  790. width: 100%;
  791. border: 1px solid #ebeef5;
  792. border-radius: 6px;
  793. overflow: hidden;
  794. }
  795. .subscribe-row {
  796. display: grid;
  797. grid-template-columns: 1fr 1fr 1fr 1fr;
  798. border-top: 1px solid #ebeef5;
  799. }
  800. .subscribe-head {
  801. background: #f5f7fa;
  802. border-top: none;
  803. }
  804. .subscribe-cell {
  805. padding: 10px 8px;
  806. font-size: 12px;
  807. color: var(--bs-heading-color);
  808. text-align: center;
  809. overflow: hidden;
  810. text-overflow: ellipsis;
  811. white-space: nowrap;
  812. }
  813. .custom-dialog-content {
  814. padding: px2rpx(20);
  815. .info-text {
  816. color: var(--bs-heading-color);
  817. font-size: px2rpx(14);
  818. line-height: px2rpx(36);
  819. }
  820. }
  821. .card-header {
  822. display: flex;
  823. justify-content: space-between;
  824. align-items: center;
  825. margin-bottom: px2rpx(12);
  826. }
  827. .header-left {
  828. display: flex;
  829. align-items: center;
  830. gap: 12rpx;
  831. }
  832. .header-title {
  833. font-size: px2rpx(14);
  834. font-weight: 600;
  835. }
  836. .header-right {
  837. display: flex;
  838. align-items: center;
  839. }
  840. .action-btn {
  841. background: #ffde02;
  842. border: none;
  843. border-radius: 50%;
  844. width: px2rpx(32);
  845. height: px2rpx(32);
  846. display: flex;
  847. align-items: center;
  848. justify-content: center;
  849. padding: 0;
  850. margin: 0;
  851. &:after {
  852. border: none;
  853. }
  854. }
  855. .dia-content {
  856. padding: 20rpx;
  857. }
  858. .content {
  859. display: flex;
  860. flex-direction: column;
  861. gap: 20rpx;
  862. }
  863. .label {
  864. font-weight: 500;
  865. margin-bottom: 8rpx;
  866. }
  867. .btn {
  868. text-align: center;
  869. background-color: rgb(var(--bs-danger-rgb));
  870. font-size: px2rpx(16);
  871. color: #fff;
  872. padding: px2rpx(10) px2rpx(20);
  873. border-radius: px2rpx(24);
  874. margin: 0;
  875. }
  876. .crm-cursor {
  877. cursor: pointer;
  878. }
  879. .link {
  880. display: flex;
  881. margin-top: 20rpx;
  882. .btn {
  883. display: flex;
  884. align-items: center;
  885. justify-content: center;
  886. height: px2rpx(35);
  887. margin: 0 px2rpx(10);
  888. }
  889. }
  890. .chart-box {
  891. background: var(--color-white);
  892. border-radius: 8px;
  893. padding: px2rpx(16);
  894. box-shadow: 0 px2rpx(4) px2rpx(12) rgba(0, 0, 0, 0.2);
  895. }
  896. .mh{
  897. height: 100%;
  898. }
  899. .chart-title {
  900. width: 100%;
  901. //display: flex;
  902. //justify-content: space-between;
  903. //align-items: flex-start;
  904. //margin-bottom: px2rpx(16);
  905. //padding-bottom: px2rpx(16);
  906. border-bottom: 1px solid #f0f0f0;
  907. }
  908. .bigtitle{
  909. font-size: px2rpx(18);
  910. }
  911. .opt-title{
  912. display: flex;
  913. align-items: center;
  914. justify-content: space-between;
  915. }
  916. .btn-opt{
  917. display: flex;
  918. height: 100%;
  919. align-items: flex-end;
  920. justify-content: flex-end;
  921. }
  922. .chart-title .time {
  923. display: flex;
  924. align-items: center;
  925. gap: px2rpx(8);
  926. font-size: px2rpx(16);
  927. margin-bottom: px2rpx(10);
  928. color: #999;
  929. }
  930. .chart-title .title {
  931. flex: 1;
  932. margin: 0;
  933. margin-bottom: px2rpx(10);
  934. }
  935. .chart-title .title .name {
  936. font-size: px2rpx(18);
  937. font-weight: 600;
  938. color: var(--bs-heading-color);
  939. text-align: center;
  940. margin-bottom: px2rpx(8);
  941. }
  942. .chart-title .title .account {
  943. font-size: px2rpx(14);
  944. color: #999;
  945. text-align: center;
  946. margin-bottom: px2rpx(8);
  947. }
  948. .chart-title .title .date {
  949. display: flex;
  950. }
  951. .chart-title .date-picker {
  952. font-size: px2rpx(12);
  953. color: #666;
  954. padding: px2rpx(6) px2rpx(12);
  955. background: #f5f5f5;
  956. border-radius: 4px;
  957. }
  958. .chart-title .operation {
  959. //gap: px2rpx(4);
  960. margin-left: px2rpx(10);
  961. color: #666;
  962. margin-bottom: px2rpx(10);
  963. height: px2rpx(35);
  964. width: px2rpx(110);
  965. line-height: px2rpx(35)!important;
  966. }
  967. .chart-container {
  968. height: px2rpx(300);
  969. }
  970. .popup-content .form {
  971. padding: px2rpx(16);
  972. }
  973. .popup-content .form-item {
  974. margin-bottom: px2rpx(20);
  975. }
  976. .popup-content .form-label {
  977. display: block;
  978. font-size: px2rpx(14);
  979. color: #666;
  980. margin-bottom: px2rpx(8);
  981. }
  982. .popup-content .picker-value {
  983. display: flex;
  984. justify-content: space-between;
  985. align-items: center;
  986. padding: px2rpx(12);
  987. background: #f5f5f5;
  988. border-radius: 4px;
  989. font-size: px2rpx(14);
  990. color: #333;
  991. }
  992. .crm-cursor {
  993. cursor: pointer;
  994. }
  995. .crm-border-radius {
  996. border-radius: 8px;
  997. }
  998. </style>