西藏巴青项目

index.vue 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. <template>
  2. <div class="screen-page ts-page" :class="{ 'is-loading': loading }">
  3. <div v-if="loadError" class="ts-error" @click="onRetry">
  4. {{ loadError }}(点击重试)
  5. </div>
  6. <!-- <div class="ts-year-bar">
  7. <label class="ts-year-bar__label">统计年份</label>
  8. <select
  9. v-model="statYear"
  10. class="ts-year-bar__select"
  11. :disabled="loading"
  12. @change="onYearChange"
  13. >
  14. <option v-for="y in availableYears" :key="y" :value="String(y)">{{ y }}年</option>
  15. </select>
  16. <span v-if="statDate" class="ts-year-bar__date">统计日 {{ statDate }}</span>
  17. <button
  18. type="button"
  19. class="ts-year-bar__refresh"
  20. :disabled="loading || refreshing"
  21. @click="onManualRefresh"
  22. >
  23. {{ refreshing ? '刷新中…' : '刷新' }}
  24. </button>
  25. </div> -->
  26. <!-- 左栏:牦牛交易 -->
  27. <div class="screen-page--home-column">
  28. <div class="screen-page--home-column-top">
  29. <div class="top_title">交易总览 ཚོང་འདོན་ཀྱི་རྒྱུན་བསྡུས།</div>
  30. <div class="ts-overview-grid">
  31. <div
  32. v-for="item in tradeOverviewItems"
  33. :key="item.key"
  34. class="ts-overview-cell"
  35. :class="{ 'ts-overview-cell--placeholder': item.placeholder }"
  36. >
  37. <div class="content_title"><div>{{ item.label }}</div></div>
  38. <div class="content_num">
  39. <strong v-if="item.placeholder" class="content_num__value">—</strong>
  40. <ScreenScrollNumber
  41. v-else
  42. class="content_num__value"
  43. :value="item.rawValue"
  44. :format="item.format"
  45. />
  46. <span v-if="item.unit" class="content_num__unit">{{ item.unit }}</span>
  47. </div>
  48. </div>
  49. </div>
  50. </div>
  51. <div class="screen-page--home-column-flex">
  52. <div class="screen-page--home-column-flex-item">
  53. <div class="flex_title">产地行情 ཐོག་མའི་གནས་ཚད།</div>
  54. <div class="flex_content flex_content--quote">
  55. <div class="quote-summary">
  56. <div class="quote-summary__item">
  57. <div class="quote-summary__label">7日均价</div>
  58. <div class="quote-summary__val">
  59. <strong>{{ formatPrice(originQuote?.avgPrice7d) }}</strong>
  60. <span class="quote-summary__unit">{{ originQuote?.priceUnitLabel || '' }}</span>
  61. </div>
  62. </div>
  63. <div class="quote-summary__item">
  64. <div class="quote-summary__label">7日最低价</div>
  65. <div class="quote-summary__val">
  66. <strong>{{ formatPrice(originQuote?.minPrice7d) }}</strong>
  67. </div>
  68. </div>
  69. <div class="quote-summary__item">
  70. <div class="quote-summary__label">7日最高价</div>
  71. <div class="quote-summary__val">
  72. <strong>{{ formatPrice(originQuote?.maxPrice7d) }}</strong>
  73. </div>
  74. </div>
  75. <div class="quote-summary__item">
  76. <div class="quote-summary__label">最近行情日</div>
  77. <div class="quote-summary__val quote-summary__val--date">
  78. <strong>{{ originQuote?.lastQuoteDate || '—' }}</strong>
  79. </div>
  80. </div>
  81. </div>
  82. <div class="quote-chart">
  83. <ScreenChart :option="originQuoteChartOption" />
  84. </div>
  85. </div>
  86. </div>
  87. <div class="screen-page--home-column-flex-item">
  88. <div class="flex_title">牦牛交易数额趋势 གཡག་ཚོང་གི་འབོར་གྱི་འགྲོ་སྟངས།</div>
  89. <div class="flex_content">
  90. <ScreenChart :option="monthlyTrendChartOption" />
  91. </div>
  92. </div>
  93. </div>
  94. <div class="screen-page--home-column-flex">
  95. <div class="screen-page--home-column-flex-item">
  96. <div class="flex_title">牦牛销售去向 གཡག་ཚོང་འདོན་གྱི་ཕྱིར་སྐྱོད།</div>
  97. <div class="flex_content">
  98. <ScreenChart :option="destinationChartOption" />
  99. </div>
  100. </div>
  101. <div class="screen-page--home-column-flex-item">
  102. <div class="flex_title">品质等级占比 སྤུན་ཤེས་རྩིས་ཀྱི་ཆ་འདུམ།</div>
  103. <div class="flex_content">
  104. <ScreenChart :option="qualityGradeChartOption" />
  105. </div>
  106. </div>
  107. </div>
  108. </div>
  109. <!-- 右栏:农资商城 -->
  110. <div class="screen-page--home-column" :class="{ 'ts-mall-unavailable': !mallStatsAvailable }">
  111. <div class="screen-page--home-column-right ts-mall-panel">
  112. <div class="top_title">
  113. 农资品类销售 ཞིང་ལས་རྒྱུ་ཆ་རིགས་ཀྱི་ཚོང་འདོན།
  114. </div>
  115. <div class="ts-mall-top">
  116. <div class="ts-mall-top__pie">
  117. <ScreenChart :option="categorySalesChartOption" />
  118. </div>
  119. <div class="ts-mall-top__hot">
  120. <div class="ts-mall-hot-title">热销农资 Top5</div>
  121. <ul v-if="hotCategoryItems.length" class="ts-mall-hot-list">
  122. <li v-for="item in hotCategoryItems" :key="item.rank" class="ts-mall-hot-list__row">
  123. <span class="ts-mall-hot-list__rank">{{ item.rank }}</span>
  124. <span class="ts-mall-hot-list__name" :title="item.categoryName">{{ item.categoryName }}</span>
  125. <span class="ts-mall-hot-list__qty">{{ formatNum(item.qty) }} 件</span>
  126. </li>
  127. </ul>
  128. <span v-else class="ts-placeholder ts-placeholder--sm">{{ mallEmptyHint }}</span>
  129. </div>
  130. </div>
  131. </div>
  132. <div class="screen-page--home-column-flex">
  133. <div class="screen-page--home-column-flex-item">
  134. <div class="flex_title">商城订单趋势 ཚོང་ཁང་གི་བཀོད་རྩིས་འགྲོ་སྟངས།</div>
  135. <div class="flex_content">
  136. <ScreenChart :option="mallOrderTrendChartOption" />
  137. </div>
  138. </div>
  139. <div class="screen-page--home-column-flex-item">
  140. <div class="flex_title">店铺入驻 ཚོང་ཁང་གི་ཞུགས་སྒོ།</div>
  141. <div class="flex_content">
  142. <ScreenChart :option="shopEntryChartOption" />
  143. </div>
  144. </div>
  145. </div>
  146. <div class="screen-page--home-column-flex">
  147. <div class="screen-page--home-column-flex-item">
  148. <div class="flex_title">消费区域排名(前5) ཟས་སྤྱོད་ས་ཁུལ་གོ་རྒྱ་(གོང་ལྔ)</div>
  149. <div class="flex_content">
  150. <ScreenChart :option="regionRankChartOption" />
  151. </div>
  152. </div>
  153. <div class="screen-page--home-column-flex-item">
  154. <div class="flex_title">消费者评价 སྤྱོད་མིའི་དཔྱད་པ།</div>
  155. <div class="flex_content">
  156. <ScreenChart :option="reviewWordCloudChartOption" />
  157. </div>
  158. </div>
  159. </div>
  160. </div>
  161. </div>
  162. </template>
  163. <script setup>
  164. import { computed, onMounted, onUnmounted, ref } from 'vue'
  165. import ScreenChart from '@/components/ScreenChart.vue'
  166. import ScreenScrollNumber from '@/components/ScreenScrollNumber.vue'
  167. import { getTradeSalesDashboard } from '@/api/tradeSales'
  168. import {
  169. buildCategorySalesPieOption,
  170. buildMallOrderTrendOption,
  171. buildOriginQuoteOption,
  172. buildQualityGradePieOption,
  173. buildRegionRankBarOption,
  174. buildReviewWordCloudOption,
  175. buildSalesDestinationBarOption,
  176. buildShopEntryPieOption,
  177. buildTradeMonthlyOption
  178. } from './chartOptions'
  179. /** 自动轮播刷新周期(对齐功能需求 §7.3,默认 3 分钟) */
  180. const DASHBOARD_REFRESH_MS = 3 * 60 * 1000
  181. const loading = ref(false)
  182. const refreshing = ref(false)
  183. const loadError = ref('')
  184. const statYear = ref(String(new Date().getFullYear()))
  185. const statDate = ref('')
  186. const availableYears = ref([new Date().getFullYear()])
  187. const tradeOverview = ref(null)
  188. const originQuote = ref(null)
  189. const tradeMonthlyTrend = ref([])
  190. const salesDestination = ref([])
  191. const qualityGrade = ref(null)
  192. const mallStatsAvailable = ref(false)
  193. const categorySales = ref(null)
  194. const hotCategoryRank = ref(null)
  195. const mallOrderTrend = ref(null)
  196. const shopEntry = ref(null)
  197. const regionRank = ref(null)
  198. const reviewWordCloud = ref(null)
  199. let dashboardTimer = null
  200. const mallAvailable = computed(() => mallStatsAvailable.value)
  201. const originQuoteChartOption = computed(() => buildOriginQuoteOption(originQuote.value))
  202. const monthlyTrendChartOption = computed(() => buildTradeMonthlyOption(tradeMonthlyTrend.value))
  203. const destinationChartOption = computed(() => buildSalesDestinationBarOption(salesDestination.value))
  204. const qualityGradeChartOption = computed(() => buildQualityGradePieOption(qualityGrade.value))
  205. const categorySalesChartOption = computed(() =>
  206. buildCategorySalesPieOption(categorySales.value, mallAvailable.value)
  207. )
  208. const mallOrderTrendChartOption = computed(() =>
  209. buildMallOrderTrendOption(mallOrderTrend.value, mallAvailable.value)
  210. )
  211. const shopEntryChartOption = computed(() => buildShopEntryPieOption(shopEntry.value, mallAvailable.value))
  212. const regionRankChartOption = computed(() => buildRegionRankBarOption(regionRank.value, mallAvailable.value))
  213. const reviewWordCloudChartOption = computed(() =>
  214. buildReviewWordCloudOption(reviewWordCloud.value, mallAvailable.value)
  215. )
  216. const hotCategoryItems = computed(() => {
  217. const items = hotCategoryRank.value?.items || []
  218. return [...items].sort((a, b) => (a.rank || 0) - (b.rank || 0))
  219. })
  220. const mallEmptyHint = computed(() => (mallStatsAvailable.value ? '暂无热销数据' : '商城统计未接入'))
  221. /** 右栏品类/热销标题:展示统计年 Y(对齐 v1.2 categorySales.statYear) */
  222. const categoryStatYearLabel = computed(() => {
  223. const y =
  224. categorySales.value?.statYear ??
  225. hotCategoryRank.value?.statYear ??
  226. statYear.value
  227. if (y === null || y === undefined || y === '') {
  228. return ''
  229. }
  230. return `${y}年`
  231. })
  232. const tradeOverviewItems = computed(() => {
  233. const o = tradeOverview.value
  234. return [
  235. {
  236. key: 'orderCount',
  237. label: '牦牛交易订单数',
  238. unit: '单',
  239. rawValue: o?.orderCount,
  240. format: 'int',
  241. placeholder: false
  242. },
  243. {
  244. key: 'tradeHeads',
  245. label: '牦牛交易量',
  246. unit: '头',
  247. rawValue: o?.tradeHeads,
  248. format: 'int',
  249. placeholder: false
  250. },
  251. {
  252. key: 'tradeAmount',
  253. label: '牦牛交易总额',
  254. unit: '元',
  255. rawValue: o?.tradeAmount,
  256. format: 'money',
  257. placeholder: false
  258. },
  259. {
  260. key: 'supplierCount',
  261. label: '入驻商铺数',
  262. unit: '家',
  263. rawValue: o?.supplierCount,
  264. format: 'int',
  265. placeholder: false
  266. },
  267. {
  268. key: 'agriOrderCount',
  269. label: '农资订单量',
  270. unit: '单',
  271. rawValue: o?.agriOrderCount,
  272. format: 'int',
  273. placeholder: o?.agriOrderCount == null
  274. },
  275. {
  276. key: 'agriSalesAmount',
  277. label: '农资销售额',
  278. unit: '元',
  279. rawValue: o?.agriSalesAmount,
  280. format: 'money',
  281. placeholder: o?.agriSalesAmount == null
  282. }
  283. ]
  284. })
  285. function formatNum(val) {
  286. if (val === null || val === undefined || val === '') {
  287. return '0'
  288. }
  289. const n = Number(val)
  290. if (Number.isNaN(n)) {
  291. return '0'
  292. }
  293. return n.toLocaleString('zh-CN')
  294. }
  295. function formatPrice(val) {
  296. if (val === null || val === undefined || val === '') {
  297. return '—'
  298. }
  299. const n = Number(val)
  300. if (Number.isNaN(n)) {
  301. return '—'
  302. }
  303. return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
  304. }
  305. function resetDashboard() {
  306. tradeOverview.value = null
  307. originQuote.value = null
  308. tradeMonthlyTrend.value = []
  309. salesDestination.value = []
  310. qualityGrade.value = null
  311. mallStatsAvailable.value = false
  312. categorySales.value = null
  313. hotCategoryRank.value = null
  314. mallOrderTrend.value = null
  315. shopEntry.value = null
  316. regionRank.value = null
  317. reviewWordCloud.value = null
  318. }
  319. function applyDashboard(data) {
  320. if (!data) {
  321. return
  322. }
  323. if (data.statYear) {
  324. statYear.value = String(data.statYear)
  325. }
  326. statDate.value = data.statDate || ''
  327. availableYears.value = data.availableYears?.length
  328. ? [...data.availableYears]
  329. : [Number(statYear.value)]
  330. tradeOverview.value = data.tradeOverview || null
  331. originQuote.value = data.originQuote || null
  332. tradeMonthlyTrend.value = data.tradeMonthlyTrend || []
  333. salesDestination.value = data.salesDestination || []
  334. qualityGrade.value = data.qualityGrade || null
  335. mallStatsAvailable.value = data.mallStatsAvailable || false
  336. categorySales.value = data.categorySales || null
  337. hotCategoryRank.value = data.hotCategoryRank || null
  338. mallOrderTrend.value = data.mallOrderTrend || null
  339. shopEntry.value = data.shopEntry || null
  340. regionRank.value = data.regionRank || null
  341. reviewWordCloud.value = data.reviewWordCloud || null
  342. }
  343. async function fetchDashboard(forceRefresh = false) {
  344. if (forceRefresh) {
  345. if (refreshing.value) {
  346. return
  347. }
  348. refreshing.value = true
  349. } else {
  350. loading.value = true
  351. resetDashboard()
  352. }
  353. loadError.value = ''
  354. try {
  355. const res = await getTradeSalesDashboard(statYear.value, forceRefresh)
  356. applyDashboard(res.data)
  357. } catch (e) {
  358. loadError.value = e?.message || '看板数据加载失败'
  359. } finally {
  360. loading.value = false
  361. refreshing.value = false
  362. }
  363. }
  364. function onYearChange() {
  365. fetchDashboard()
  366. }
  367. function onManualRefresh() {
  368. fetchDashboard(true)
  369. }
  370. function onRetry() {
  371. fetchDashboard()
  372. }
  373. function startDashboardTimer() {
  374. dashboardTimer = setInterval(() => {
  375. fetchDashboard(true)
  376. }, DASHBOARD_REFRESH_MS)
  377. }
  378. onMounted(() => {
  379. fetchDashboard()
  380. startDashboardTimer()
  381. })
  382. onUnmounted(() => {
  383. if (dashboardTimer) {
  384. clearInterval(dashboardTimer)
  385. dashboardTimer = null
  386. }
  387. })
  388. </script>
  389. <style scoped>
  390. .ts-page {
  391. position: relative;
  392. width: 100%;
  393. height: 100%;
  394. box-sizing: border-box;
  395. padding: 20px;
  396. display: flex;
  397. flex-direction: row;
  398. justify-content: space-between;
  399. }
  400. .ts-page.is-loading {
  401. opacity: 0.92;
  402. }
  403. .ts-error {
  404. position: absolute;
  405. top: 4px;
  406. left: 50%;
  407. transform: translateX(-50%);
  408. z-index: 10;
  409. padding: 4px 12px;
  410. font-size: 12px;
  411. color: #ffb4b4;
  412. background: rgba(80, 20, 20, 0.75);
  413. border-radius: 4px;
  414. cursor: pointer;
  415. }
  416. .ts-year-bar {
  417. position: absolute;
  418. top: 8px;
  419. right: 24px;
  420. z-index: 5;
  421. display: flex;
  422. align-items: center;
  423. gap: 8px;
  424. }
  425. .ts-year-bar__label {
  426. font-size: 12px;
  427. color: var(--screen-text-secondary, #a8d4c8);
  428. }
  429. .ts-year-bar__select {
  430. min-width: 88px;
  431. height: 28px;
  432. padding: 0 8px;
  433. font-size: 13px;
  434. color: #e8eef5;
  435. background: rgba(4, 48, 40, 0.85);
  436. border: 1px solid var(--screen-line-dim, rgba(61, 217, 176, 0.42));
  437. border-radius: 4px;
  438. outline: none;
  439. }
  440. .ts-year-bar__date {
  441. font-size: 11px;
  442. color: rgba(168, 212, 200, 0.75);
  443. }
  444. .ts-year-bar__refresh {
  445. height: 28px;
  446. padding: 0 10px;
  447. font-size: 12px;
  448. color: #e8eef5;
  449. background: rgba(4, 48, 40, 0.85);
  450. border: 1px solid var(--screen-line-dim, rgba(61, 217, 176, 0.42));
  451. border-radius: 4px;
  452. cursor: pointer;
  453. }
  454. .ts-year-bar__refresh:disabled {
  455. opacity: 0.6;
  456. cursor: not-allowed;
  457. }
  458. .screen-page--home-column {
  459. width: 617px;
  460. height: 100%;
  461. display: flex;
  462. flex-direction: column;
  463. justify-content: space-between;
  464. }
  465. .screen-page--home-column-top {
  466. width: 617px;
  467. height: 201px;
  468. display: flex;
  469. flex-direction: column;
  470. box-sizing: border-box;
  471. background: url('../../assets/sale/sale.png') no-repeat center center;
  472. background-size: 100% 100%;
  473. }
  474. .screen-page--home-column-right {
  475. width: 617px;
  476. height: 201px;
  477. background: url('../../assets/pro/pro.png') no-repeat center center;
  478. background-size: 100% 100%;
  479. }
  480. .ts-mall-panel {
  481. display: flex;
  482. flex-direction: column;
  483. }
  484. .ts-mall-stat-year {
  485. font-size: 12px;
  486. color: rgba(168, 212, 200, 0.8);
  487. margin-left: 4px;
  488. }
  489. .ts-mall-top {
  490. flex: 1;
  491. min-height: 0;
  492. display: flex;
  493. flex-direction: row;
  494. gap: 8px;
  495. padding: 0 12px 10px 28px;
  496. box-sizing: border-box;
  497. }
  498. .ts-mall-top__pie {
  499. flex: 1.1;
  500. min-width: 0;
  501. min-height: 0;
  502. }
  503. .ts-mall-top__hot {
  504. flex: 0.9;
  505. min-width: 0;
  506. display: flex;
  507. flex-direction: column;
  508. padding: 4px 6px;
  509. background: rgba(4, 48, 40, 0.35);
  510. border-radius: 4px;
  511. border: 1px solid rgba(61, 217, 176, 0.18);
  512. }
  513. .ts-mall-hot-title {
  514. font-size: 11px;
  515. color: #a8d4c8;
  516. margin-bottom: 4px;
  517. flex-shrink: 0;
  518. }
  519. .ts-mall-hot-list {
  520. list-style: none;
  521. margin: 0;
  522. padding: 0;
  523. flex: 1;
  524. min-height: 0;
  525. overflow: hidden;
  526. }
  527. .ts-mall-hot-list__row {
  528. display: flex;
  529. align-items: center;
  530. gap: 6px;
  531. font-size: 10px;
  532. color: #e8eef5;
  533. line-height: 1.6;
  534. }
  535. .ts-mall-hot-list__rank {
  536. width: 16px;
  537. text-align: center;
  538. color: #ecd27b;
  539. flex-shrink: 0;
  540. }
  541. .ts-mall-hot-list__name {
  542. flex: 1;
  543. min-width: 0;
  544. overflow: hidden;
  545. text-overflow: ellipsis;
  546. white-space: nowrap;
  547. color: #a8d4c8;
  548. }
  549. .ts-mall-hot-list__qty {
  550. flex-shrink: 0;
  551. color: #5ef0c8;
  552. font-weight: 600;
  553. }
  554. .ts-placeholder {
  555. flex: 1;
  556. display: flex;
  557. align-items: center;
  558. justify-content: center;
  559. padding: 0 24px;
  560. text-align: center;
  561. font-size: 14px;
  562. color: rgba(168, 212, 200, 0.75);
  563. }
  564. .ts-placeholder--sm {
  565. font-size: 13px;
  566. }
  567. .screen-page--home-column-flex {
  568. width: 617px;
  569. height: 223px;
  570. display: flex;
  571. justify-content: space-between;
  572. }
  573. .screen-page--home-column-flex-item {
  574. width: 304px;
  575. height: 223px;
  576. background: url('../../assets/home/mini_box.png') no-repeat center center;
  577. background-size: 100% 100%;
  578. }
  579. .top_title {
  580. width: 100%;
  581. height: 35px;
  582. box-sizing: border-box;
  583. font-size: 18px;
  584. color: #fff;
  585. line-height: 35px;
  586. padding-left: 100px;
  587. margin-bottom: 8px;
  588. flex-shrink: 0;
  589. }
  590. .ts-overview-grid {
  591. flex: 1;
  592. min-height: 0;
  593. box-sizing: border-box;
  594. padding: 6px 20px 14px 32px;
  595. display: grid;
  596. grid-template-columns: repeat(3, 1fr);
  597. grid-template-rows: repeat(2, 1fr);
  598. gap: 4px 40px;
  599. }
  600. .ts-overview-cell {
  601. display: flex;
  602. flex-direction: column;
  603. align-items: flex-end;
  604. justify-content: center;
  605. min-height: 0;
  606. width: 100%;
  607. text-align: right;
  608. box-sizing: border-box;
  609. padding-right: 10px;
  610. }
  611. .ts-overview-cell .content_title {
  612. width: 100%;
  613. text-align: right;
  614. }
  615. .ts-overview-cell .content_num {
  616. width: 100%;
  617. display: flex;
  618. align-items: baseline;
  619. justify-content: flex-end;
  620. gap: 6px;
  621. margin: 4px 0 0;
  622. line-height: 1.3;
  623. }
  624. .content_num__value {
  625. flex: 1;
  626. min-width: 0;
  627. text-align: right;
  628. font-size: 24px;
  629. font-weight: 600;
  630. background: linear-gradient(to bottom, #98e9aa, #ecd27b);
  631. -webkit-background-clip: text;
  632. background-clip: text;
  633. color: transparent;
  634. }
  635. .content_num :deep(.screen-scroll-number) {
  636. flex: 1;
  637. min-width: 0;
  638. justify-content: flex-end;
  639. font-size: 24px;
  640. font-weight: 600;
  641. }
  642. .content_num :deep(.screen-scroll-number__strip),
  643. .content_num :deep(.screen-scroll-number__sep) {
  644. background: linear-gradient(to bottom, #98e9aa, #ecd27b);
  645. -webkit-background-clip: text;
  646. background-clip: text;
  647. color: transparent;
  648. }
  649. .content_num__unit {
  650. flex-shrink: 0;
  651. font-size: 12px;
  652. color: #a8d4c8;
  653. line-height: 1.2;
  654. text-align: right;
  655. }
  656. .ts-overview-cell--placeholder .content_num__value {
  657. font-size: 18px;
  658. background: none;
  659. -webkit-background-clip: unset;
  660. background-clip: unset;
  661. color: #6a9f90;
  662. }
  663. .content_title {
  664. font-size: 11px;
  665. color: #a8d4c8;
  666. line-height: 1.3;
  667. }
  668. .flex_title {
  669. width: 100%;
  670. box-sizing: border-box;
  671. height: 35px;
  672. font-size: 16px;
  673. color: #fff;
  674. line-height: 35px;
  675. padding-left: 45px;
  676. margin-bottom: 5px;
  677. }
  678. .flex_content {
  679. width: 100%;
  680. height: 180px;
  681. box-sizing: border-box;
  682. padding: 5px;
  683. }
  684. .flex_content--quote {
  685. display: flex;
  686. flex-direction: column;
  687. padding: 4px 6px;
  688. }
  689. .quote-summary {
  690. display: flex;
  691. flex-direction: row;
  692. flex-wrap: wrap;
  693. gap: 4px;
  694. min-height: 42px;
  695. flex-shrink: 0;
  696. }
  697. .quote-summary__item {
  698. flex: 1 1 calc(50% - 4px);
  699. min-width: 0;
  700. display: flex;
  701. flex-direction: column;
  702. align-items: center;
  703. justify-content: center;
  704. background: rgba(4, 48, 40, 0.45);
  705. border-radius: 4px;
  706. border: 1px solid rgba(61, 217, 176, 0.2);
  707. padding: 2px 0;
  708. }
  709. .quote-summary__label {
  710. font-size: 9px;
  711. color: #a8d4c8;
  712. }
  713. .quote-summary__val {
  714. font-size: 10px;
  715. color: #fff;
  716. }
  717. .quote-summary__val strong {
  718. font-size: 12px;
  719. background: linear-gradient(to bottom, #98e9aa, #ecd27b);
  720. -webkit-background-clip: text;
  721. background-clip: text;
  722. color: transparent;
  723. }
  724. .quote-summary__val--date strong {
  725. font-size: 10px;
  726. }
  727. .quote-summary__unit {
  728. font-size: 9px;
  729. color: #a8d4c8;
  730. margin-left: 2px;
  731. }
  732. .quote-chart {
  733. flex: 1;
  734. min-height: 0;
  735. }
  736. </style>