IndicatorOverview.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. <script setup lang="ts">
  2. /**
  3. * @Name: IndicatorOverview.vue
  4. * @Description: 分析页-指标总览
  5. * @Author: Cheney
  6. */
  7. import { inject, onBeforeUnmount, reactive, ref, Ref, watch } from 'vue';
  8. import { Bottom, Top } from '@element-plus/icons-vue';
  9. import * as api from '/@/views/searchTerm/analysisPage/api';
  10. import emitter from '/@/utils/emitter';
  11. const filter = inject<Ref>('filter');
  12. const metricLoading = ref(false);
  13. const responseData = ref(null);
  14. const indicator = reactive([
  15. {
  16. title: '',
  17. value: '',
  18. compare: '',
  19. color: '#edf6fd',
  20. },
  21. {
  22. title: '',
  23. value: '',
  24. compare: '',
  25. color: '#eefdf0',
  26. },
  27. {
  28. title: '',
  29. value: '',
  30. compare: '',
  31. color: '#fff7ed',
  32. },
  33. {
  34. title: '',
  35. value: '',
  36. compare: '',
  37. color: '#f0f0fe',
  38. },
  39. ]);
  40. // 指标与返回数据字段的映射关系
  41. const mapping = {
  42. imp: {
  43. title: '曝光量',
  44. key: 0, // indicator 数组中对应的索引
  45. kw: 'Impressions',
  46. },
  47. click: {
  48. title: '点击率',
  49. key: 1,
  50. kw: 'Clicks',
  51. },
  52. cart_add: {
  53. title: '加购份额',
  54. key: 2,
  55. kw: 'Cart_Adds',
  56. },
  57. purchases: {
  58. title: '购买份额',
  59. key: 3,
  60. kw: 'Purchases',
  61. },
  62. };
  63. onBeforeUnmount(() => {
  64. emitter.all.clear();
  65. });
  66. emitter.on('QueryCondition-sendRequest', () => {
  67. fetchIndicatorData();
  68. });
  69. watch(responseData, (newData) => {
  70. if (newData) {
  71. for (const key in mapping) {
  72. if (newData[key]) {
  73. const index = mapping[key].key;
  74. indicator[index].title = mapping[key].title;
  75. indicator[index].value = newData[key][`${mapping[key].kw + '_A_B_Share'}`];
  76. indicator[index].compare = newData[key][`diff_${mapping[key].kw + '_A_B_Share'}`];
  77. }
  78. }
  79. } else {
  80. indicator.forEach((item) => {
  81. item.title = '暂无数据';
  82. item.value = '--';
  83. item.compare = '--';
  84. });
  85. }
  86. });
  87. async function fetchIndicatorData() {
  88. metricLoading.value = true;
  89. const query = {
  90. date_start: filter.value.reportDate[0],
  91. date_end: filter.value.reportDate[1],
  92. layer_type: filter.value.layerType,
  93. search_term: filter.value.searchTerm,
  94. report_range: filter.value.reportType,
  95. [filter.value.layerType.split('_')[0]]: filter.value.variable,
  96. };
  97. try {
  98. const response = await api.getAsinMetrics(query);
  99. responseData.value = response.data;
  100. } catch (error) {
  101. console.error('==Error==', error);
  102. } finally {
  103. metricLoading.value = false;
  104. }
  105. }
  106. </script>
  107. <template>
  108. <el-card
  109. v-loading="metricLoading"
  110. shadow="hover"
  111. body-class="flex justify-between w-full"
  112. style="border: none; margin-bottom: 10px">
  113. <el-card
  114. v-for="(item, index) in indicator"
  115. :key="index"
  116. body-class="flex flex-col items-center"
  117. class="flex-1 mx-1"
  118. :style="{
  119. background: item.color
  120. ? `linear-gradient(to top, ${item.color}, transparent)`
  121. : 'linear-gradient(to top, #fff, transparent)',
  122. }">
  123. <div class="text-2xl font-bold mb-1.5">{{ item.title || '暂无数据' }}</div>
  124. <div class="font-medium text-2xl mb-1.5">
  125. {{ item.value !== null && item.value !== undefined && item.value !== '' ? item.value : '--' }}
  126. <span v-if="item.value !== null && item.value !== undefined && item.value !== '' && item.value !== '--'">%</span>
  127. </div>
  128. <div>
  129. <span class="mr-2 font-medium" style="color: #9ca3af">较上期</span>
  130. <template v-if="item.compare !== '-'">
  131. <el-icon
  132. v-if="item.compare && item.compare !== '--'"
  133. :color="Number(item.compare) > 0 ? '#59b939' : '#e36f53'"
  134. style="display: inline-block; padding-top: 2px">
  135. <component :is="Number(item.compare) > 0 ? Top : Bottom" />
  136. </el-icon>
  137. <span
  138. class="font-medium"
  139. :style="{
  140. color:
  141. item.compare && item.compare !== '--'
  142. ? Number(item.compare) > 0
  143. ? '#59b939'
  144. : Number(item.compare) < 0
  145. ? '#e36f53'
  146. : ''
  147. : '',
  148. }">
  149. {{ item.compare !== null && item.compare !== undefined && item.compare !== '' ? item.compare : '--' }}
  150. </span>
  151. <span
  152. v-if="item.compare && item.compare !== '--'"
  153. :style="{
  154. color: Number(item.compare) > 0 ? '#59b939' : Number(item.compare) < 0 ? '#e36f53' : '',
  155. }"
  156. >%</span
  157. >
  158. </template>
  159. <template v-else>
  160. <span class="font-medium">无数据</span>
  161. </template>
  162. </div>
  163. </el-card>
  164. </el-card>
  165. </template>
  166. <style scoped></style>