|
@@ -0,0 +1,191 @@
|
|
|
+<script setup lang="ts">
|
|
|
+/**
|
|
|
+ * @Name: WordCloud.vue
|
|
|
+ * @Description: 搜索词-分析页-词云图
|
|
|
+ * @Author: Cheney
|
|
|
+ */
|
|
|
+import { inject, onBeforeUnmount, reactive, Ref, ref } from 'vue';
|
|
|
+import * as api from '/@/views/searchTerm/analysisPage/api';
|
|
|
+import * as echarts from 'echarts';
|
|
|
+import 'echarts-wordcloud';
|
|
|
+import emitter from '/@/utils/emitter';
|
|
|
+
|
|
|
+const filter = inject<Ref>('filter');
|
|
|
+const loading = ref(false);
|
|
|
+
|
|
|
+const wordCloudFilter = reactive({
|
|
|
+ typeSelect1: 'positive',
|
|
|
+ typeSelect2: 'Impressions_Total_Count',
|
|
|
+});
|
|
|
+
|
|
|
+let responseData = null;
|
|
|
+const chartRef = ref<HTMLElement | null>(null);
|
|
|
+let chart: echarts.ECharts | null = null;
|
|
|
+let resizeObserver: ResizeObserver | null = null;
|
|
|
+const hasData = ref(true);
|
|
|
+
|
|
|
+onBeforeUnmount(() => {
|
|
|
+ emitter.all.clear();
|
|
|
+ // 清理 ResizeObserver
|
|
|
+ if (resizeObserver) {
|
|
|
+ resizeObserver.disconnect();
|
|
|
+ }
|
|
|
+ if (chart) {
|
|
|
+ chart.dispose();
|
|
|
+ chart = null;
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+emitter.on('QueryCondition-sendRequest', () => {
|
|
|
+ fetchWordCloudData();
|
|
|
+});
|
|
|
+
|
|
|
+async function fetchWordCloudData() {
|
|
|
+ loading.value = true;
|
|
|
+ const query = {
|
|
|
+ layer_type: filter.value.layerType,
|
|
|
+ report_range: filter.value.reportType,
|
|
|
+ searchTerm_type: wordCloudFilter.typeSelect1,
|
|
|
+ };
|
|
|
+ try {
|
|
|
+ responseData = await api.getWordCloudData(query);
|
|
|
+ if (!responseData.data || responseData.data.length === 0) {
|
|
|
+ // 处理空数据的情况
|
|
|
+ hasData.value = false;
|
|
|
+ if (chart) {
|
|
|
+ chart.clear();
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ hasData.value = true;
|
|
|
+
|
|
|
+ const selectedMetric = wordCloudFilter.typeSelect2;
|
|
|
+ // 生成词云数据
|
|
|
+ const wordCloudData = responseData.data.map((item) => ({
|
|
|
+ name: item.root_search_term,
|
|
|
+ value: item[selectedMetric],
|
|
|
+ }));
|
|
|
+
|
|
|
+ let option = {
|
|
|
+ title: {
|
|
|
+ text: '搜索词词云图',
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ show: true,
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: 'wordCloud',
|
|
|
+ // 词云图的形状
|
|
|
+ shape: 'circle', // 可以是 'circle', 'cardioid', 'diamond', 'triangle-forward', 'triangle', 'pentagon', 'star'
|
|
|
+ // 控制字体大小
|
|
|
+ sizeRange: [12, 45],
|
|
|
+ // 文本旋转角度
|
|
|
+ rotationRange: [-90, 90],
|
|
|
+ data: wordCloudData,
|
|
|
+ emphasis: {
|
|
|
+ focus: 'self', // 聚焦悬浮项
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 50, // 放大效果的字体大小
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ backgroundColor: '#fff',
|
|
|
+ };
|
|
|
+ // console.log('selectedMetric', selectedMetric)
|
|
|
+
|
|
|
+ console.log('wordCloudData', wordCloudData);
|
|
|
+ initChart(option);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('==Error==', error);
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function initChart(opt: echarts.EChartsCoreOption) {
|
|
|
+ if (!chart) {
|
|
|
+ chart = echarts.init(chartRef.value);
|
|
|
+ }
|
|
|
+ chart.setOption(opt);
|
|
|
+
|
|
|
+ // 添加 ResizeObserver 以处理图表大小变化
|
|
|
+ if (!resizeObserver) {
|
|
|
+ resizeObserver = new ResizeObserver(() => {
|
|
|
+ chart?.resize();
|
|
|
+ });
|
|
|
+ resizeObserver.observe(chartRef.value);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleSelectChange() {
|
|
|
+ fetchWordCloudData();
|
|
|
+}
|
|
|
+
|
|
|
+function changeChart() {
|
|
|
+ const selectedMetric = wordCloudFilter.typeSelect2;
|
|
|
+ // 生成词云数据
|
|
|
+ const wordCloudData = responseData.data.map((item) => ({
|
|
|
+ name: item.root_search_term,
|
|
|
+ value: item[selectedMetric],
|
|
|
+ }));
|
|
|
+
|
|
|
+ let option = {
|
|
|
+ tooltip: {
|
|
|
+ show: true,
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ type: 'wordCloud',
|
|
|
+ // 词云图的形状
|
|
|
+ shape: 'circle', // 可以是 'circle', 'cardioid', 'diamond', 'triangle-forward', 'triangle', 'pentagon', 'star'
|
|
|
+ // 控制字体大小
|
|
|
+ sizeRange: [12, 45],
|
|
|
+ // 文本旋转角度
|
|
|
+ rotationRange: [-90, 90],
|
|
|
+ data: wordCloudData,
|
|
|
+ emphasis: {
|
|
|
+ focus: 'self', // 聚焦悬浮项
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 50, // 放大效果的字体大小
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ backgroundColor: '#fff',
|
|
|
+ };
|
|
|
+
|
|
|
+ initChart(option);
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <el-card shadow="never" v-loading="loading" class="mt-5">
|
|
|
+ <div class="flex gap-5 mb-4 justify-center">
|
|
|
+ <div>
|
|
|
+ <span class="font-medium mr-0.5">类型 </span>
|
|
|
+ <el-select v-model="wordCloudFilter.typeSelect1" @change="handleSelectChange" style="width: 130px">
|
|
|
+ <el-option label="positive" value="positive" />
|
|
|
+ <el-option label="negative" value="negative" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <span class="font-medium mr-0.5">指标 </span>
|
|
|
+ <el-select v-model="wordCloudFilter.typeSelect2" @change="changeChart" style="width: 130px">
|
|
|
+ <el-option label="曝光次数" value="Impressions_Total_Count" />
|
|
|
+ <el-option label="点击次数" value="Clicks_Total_Count" />
|
|
|
+ <el-option label="加购次数" value="Cart_Adds_Total_Count" />
|
|
|
+ <el-option label="购买次数" value="Purchases_Total_Count" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!--</div>-->
|
|
|
+ <div v-show="!loading && !hasData" class="no-data-message" style="min-height: 500px">
|
|
|
+ <el-empty />
|
|
|
+ </div>
|
|
|
+ <div v-show="hasData" ref="chartRef" style="width: 100%; height: 500px"></div>
|
|
|
+ </el-card>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style scoped></style>
|