dashboard.vue 31 KB

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