adStruct.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. <template>
  2. <div v-loading="loading">
  3. <el-row :gutter="5">
  4. <el-col :span="7">
  5. <div>
  6. <!--<TextSelector v-model="modelValue" :options="computedPieOptions" @change="changePie" style="margin-top: 5px"/>-->
  7. <el-select v-model="modelValue" class="m-2" size="small" @change="changePie" style="width: 120px">
  8. <el-option v-for="item in computedPieOptions" :key="item.value" :label="item.label" :value="item.value" />
  9. </el-select>
  10. </div>
  11. <div ref="pie" style="height: 400px"></div>
  12. </el-col>
  13. <el-col :span="17">
  14. <div style="margin-left: 40%">
  15. <span style="background: #3a83f7; width: 18px; height: 10px; margin-top: 8px; display: inline-block; border-radius: 3px"></span>
  16. <TextSelector v-model="barModelValue1" :options="computedBarOptions1" @change="changeBarOne" style="margin-top: 5px; margin-left: 8px" />
  17. <span
  18. style="
  19. background: #f19a37;
  20. width: 18px;
  21. height: 10px;
  22. margin-top: 8px;
  23. margin-left: 20px;
  24. display: inline-block;
  25. border-radius: 3px;
  26. "></span>
  27. <TextSelector v-model="barModelValue2" :options="computedBarOptions2" @change="changeBarTwo" style="margin-top: 5px; margin-left: 8px" />
  28. </div>
  29. <div ref="bar" style="height: 400px"></div>
  30. </el-col>
  31. </el-row>
  32. </div>
  33. </template>
  34. <script setup>
  35. import * as echarts from 'echarts'
  36. import { computed, inject, onMounted, ref, watch } from 'vue'
  37. import { createDisabledOptions } from '../../../utils/dropdowndisable'
  38. import TextSelector from '/@/components/TextSelector/index.vue'
  39. import { useShopInfo } from '/@/stores/shopInfo'
  40. import { getAdStructureData } from '/@/views/adManage/sb/campaigns/api'
  41. import { barOptionsMap, metricMap, pieOptions, sbBarOptions1, sbBarOptions2 } from '/@/views/adManage/utils/enum'
  42. const shopInfo = useShopInfo()
  43. let pieChart = ref()
  44. let barChart = ref()
  45. const pie = ref()
  46. const bar = ref()
  47. const loading = ref(true)
  48. const dateRange = inject('dateRange')
  49. // 下拉框相关
  50. let modelValue = ref(pieOptions[0].value)
  51. let barModelValue1 = ref(sbBarOptions1[0].value)
  52. let barModelValue2 = ref(sbBarOptions2[2].value)
  53. onMounted(async () => {
  54. barChart = echarts.init(bar.value)
  55. pieChart = echarts.init(pie.value)
  56. window.addEventListener('resize', resizeChart) // 监听窗口大小变化,调整图表大小
  57. setTimeout(() => {
  58. resizeChart()
  59. }, 0)
  60. await initPieBarData()
  61. initChart()
  62. })
  63. // 获取总数据
  64. let allData = null
  65. async function setAdStructureData() {
  66. allData = await getAdStructureData({ startDate: dateRange.value[0], endDate: dateRange.value[1], profileId: shopInfo.profile.profile_id })
  67. return allData.data
  68. }
  69. // 饼图总数据和柱状图总数据
  70. let pieData
  71. let barData
  72. let pieBarData
  73. // 柱状图初始数据
  74. let ACOSList
  75. let SpendList
  76. let xAxisList
  77. let xAxisMapList
  78. async function initPieBarData() {
  79. pieBarData = await setAdStructureData()
  80. pieData = [
  81. { value: pieBarData.pie_data[0].Spend, name: '品牌视频' },
  82. { value: pieBarData.pie_data[1].Spend, name: '商品集' },
  83. { value: pieBarData.pie_data[2].Spend, name: '视频' },
  84. ]
  85. barData = pieBarData.line_data
  86. // 柱状图初始化数据
  87. ACOSList = barData.map((item) => item.ACOS)
  88. SpendList = barData.map((item) => item.Spend)
  89. // 将x轴映射为中文
  90. xAxisList = barData.map((item) => item.Classification)
  91. const classificationMap = {
  92. BROAD: '关键词-广泛',
  93. THEME: '主题',
  94. category: '品类',
  95. EXACT: '关键词-精准',
  96. asin: '商品',
  97. PHRASE: '关键词-词组',
  98. }
  99. xAxisMapList = xAxisList.map((item) => classificationMap[item])
  100. loading.value = false
  101. }
  102. // 重置图像
  103. let flag = ref()
  104. let option
  105. let option2
  106. // 点击下拉框后重新渲染饼图
  107. function changePie(newValue) {
  108. modelValue.value = newValue
  109. flag.value = modelValue.value
  110. option2.series[0].data = [
  111. { value: pieBarData.pie_data[0][flag.value], name: '品牌视频' },
  112. { value: pieBarData.pie_data[1][flag.value], name: '商品集' },
  113. { value: pieBarData.pie_data[2][flag.value], name: '视频' },
  114. ]
  115. pieChart.setOption(option2)
  116. }
  117. // 点击下拉框后重新渲染柱状图
  118. function changeBarOne(newValue) {
  119. barModelValue1.value = newValue
  120. updateBarChart()
  121. }
  122. function changeBarTwo(newValue) {
  123. barModelValue2.value = newValue
  124. updateBarChart()
  125. }
  126. function updateBarChart() {
  127. const barValues1 = barData.map((item) => item[barModelValue1.value])
  128. const barValues2 = barData.map((item) => item[barModelValue2.value])
  129. option.series[0].data = barValues1
  130. option.series[1].data = barValues2
  131. // 同时更新系列的name属性,以确保鼠标悬停时显示的文本正确
  132. option.series[0].name = barOptionsMap[barModelValue1.value] || barModelValue1.value
  133. option.series[1].name = barOptionsMap[barModelValue2.value] || barModelValue2.value
  134. barChart.setOption(option)
  135. }
  136. // 监听时间变化重新渲染表格
  137. watch(dateRange, async () => {
  138. if (dateRange.value) {
  139. loading.value = true
  140. const resp = await setAdStructureData()
  141. updatePieChartData(resp)
  142. updateBarChartData(resp)
  143. loading.value = false
  144. }
  145. })
  146. // 根据新数据和当前下拉框选择更新 饼图数据
  147. function updatePieChartData(resp) {
  148. option2.series[0].data = [
  149. { value: resp.pie_data[0][modelValue.value], name: '品牌视频' },
  150. { value: resp.pie_data[1][modelValue.value], name: '商品集' },
  151. { value: resp.pie_data[2][modelValue.value], name: '视频' },
  152. ]
  153. pieChart.setOption(option2)
  154. }
  155. // 根据新数据和当前下拉框选择更新 柱状图数据
  156. function updateBarChartData(resp) {
  157. const barValues1 = resp.line_data.map((item) => item[barModelValue1.value])
  158. const barValues2 = resp.line_data.map((item) => item[barModelValue2.value])
  159. option.series[0].data = barValues1
  160. option.series[1].data = barValues2
  161. barChart.setOption(option)
  162. }
  163. const computedBarOptions1 = computed(() => createDisabledOptions(sbBarOptions1, barModelValue2.value, barModelValue1.value))
  164. const computedBarOptions2 = computed(() => createDisabledOptions(sbBarOptions2, barModelValue1.value, barModelValue2.value))
  165. const computedPieOptions = computed(() => createDisabledOptions(pieOptions, modelValue.value))
  166. // 初始化图表
  167. function initChart() {
  168. // 柱状图配置
  169. option = {
  170. tooltip: {
  171. trigger: 'axis',
  172. axisPointer: {
  173. type: 'shadow',
  174. },
  175. rich: {
  176. b: {
  177. color: '#4C5058',
  178. fontSize: 15,
  179. fontWeight: 'bold',
  180. lineHeight: 33,
  181. },
  182. },
  183. },
  184. toolbox: {
  185. feature: {
  186. saveAsImage: { yAxisIndex: 'none' },
  187. },
  188. },
  189. grid: { top: 55, right: 60, bottom: 55, left: 55 },
  190. xAxis: [
  191. {
  192. type: 'category',
  193. boundaryGap: true,
  194. data: xAxisMapList,
  195. // axisLabel: {
  196. // rotate: -30, // 将标签旋转
  197. // fontSize: 13
  198. // }
  199. },
  200. ],
  201. yAxis: [
  202. {
  203. type: 'value',
  204. // name: '数据1',
  205. // axisLabel: {
  206. // formatter: '{value} %'
  207. // },
  208. axisLine: {
  209. show: true,
  210. lineStyle: {
  211. color: '#3a83f7', // 第一个 Y 轴的颜色
  212. },
  213. },
  214. },
  215. {
  216. type: 'value',
  217. // name: '数据2',
  218. splitLine: {
  219. show: false,
  220. },
  221. // axisLabel: {
  222. // formatter: '{value} 单位2'
  223. // },
  224. axisLine: {
  225. show: true,
  226. lineStyle: {
  227. color: '#f19a37', // 第一个 Y 轴的颜色
  228. },
  229. },
  230. },
  231. ],
  232. series: [
  233. {
  234. name: barOptionsMap[barModelValue1.value],
  235. type: 'bar',
  236. barWidth: 15,
  237. data: ACOSList,
  238. yAxisIndex: 0,
  239. itemStyle: {
  240. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  241. { offset: 0, color: '#3a83f7' }, // 起始的鲜亮蓝色
  242. { offset: 0.5, color: '#5a9ef4' }, // 中间色,中度蓝色
  243. { offset: 1, color: '#8ab6f1' }, // 结束的浅蓝色
  244. ]),
  245. // 柱状图圆角
  246. borderRadius: [4, 4, 4, 4],
  247. },
  248. },
  249. {
  250. name: barOptionsMap[barModelValue2.value],
  251. type: 'bar',
  252. barWidth: 15,
  253. data: SpendList,
  254. yAxisIndex: 1,
  255. itemStyle: {
  256. color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  257. { offset: 0, color: '#f19a37' },
  258. { offset: 0.5, color: '#f7b96c' }, // 中间色,浅橙色
  259. { offset: 1, color: 'rgb(234, 207, 135)' }, // 结束的浅黄色
  260. ]),
  261. // 柱状图圆角
  262. borderRadius: [4, 4, 4, 4],
  263. },
  264. },
  265. ],
  266. }
  267. barChart.setOption(option)
  268. // 饼图配置
  269. option2 = {
  270. tooltip: {
  271. show: false,
  272. trigger: 'item',
  273. },
  274. series: [
  275. {
  276. type: 'pie',
  277. radius: ['20%', '45%'],
  278. avoidLabelOverlap: false,
  279. itemStyle: {
  280. // borderRadius: 10,
  281. borderWidth: 1, // 设置边框的宽度
  282. borderColor: '#fff', // 将边框颜色设置为白色或图表背景颜色
  283. },
  284. emphasis: {
  285. label: {
  286. show: true,
  287. // fontSize: 40,
  288. fontWeight: 'bold',
  289. },
  290. },
  291. label: {
  292. show: true,
  293. position: 'outside', // 标签显示在外侧
  294. // formatter: `{b}\n{b|${metricMap[modelValue.value]}:{c}}\n{d}%`, // 标签文本格式器
  295. formatter: (params) => {
  296. return params.name + '\n' + '{b|' + metricMap[modelValue.value] + ':}' + '{b|' + params.data.value + '}' + '\n' + params.percent + '%'
  297. },
  298. rich: {
  299. b: {
  300. color: '#4C5058',
  301. fontSize: 15,
  302. fontWeight: 'bold',
  303. lineHeight: 33,
  304. },
  305. },
  306. },
  307. labelLine: {
  308. normal: {
  309. show: true,
  310. },
  311. },
  312. data: pieData,
  313. },
  314. ],
  315. }
  316. pieChart.setOption(option2)
  317. resizeChart()
  318. }
  319. function resizeChart() {
  320. barChart.resize()
  321. pieChart.resize()
  322. }
  323. defineExpose({ resizeChart })
  324. </script>
  325. <style scoped></style>