<template>
  <div class="word-cloud-container">
    <svg :width="width" :height="height" ref="svgRef">
      </svg>
    <div v-if="!words || words.length === 0" class="no-data-message">
      {{ emptyText }}
    </div>
  </div>
</template>

<script>
import { defineComponent, ref, watch, onMounted, nextTick } from 'vue';
import * as d3 from 'd3';
import cloud from 'd3-cloud';

// 默认颜色数组，您可以根据需要扩展
const defaultColors = [
  '#f56c6c', // 红色
  '#e6a23c', // 橙色
  '#67c23a', // 绿色
  '#409eff', // 蓝色
  '#909399', // 灰色
  '#b3a2c7', // 紫色
  '#49a37e', // 军绿
];

export default defineComponent({
  name: 'WordCloud',
  props: {
    // 词汇数组，例如：['通用人工智能', 'AI芯片', '出口管制']
    words: {
      type: Array,
      required: true,
      default: () => [],
    },
    // 词云容器的宽度
    width: {
      type: [Number, String],
      default: 600,
    },
    // 词云容器的高度
    height: {
      type: [Number, String],
      default: 400,
    },
    // 词汇的最小字号
    minFontSize: {
      type: Number,
      default: 16,
    },
    // 词汇的最大字号
    maxFontSize: {
      type: Number,
      default: 50,
    },
    // 词汇颜色数组
    colors: {
      type: Array,
      default: () => defaultColors,
    },
    // 词汇为空时的提示文本
    emptyText: {
      type: String,
      default: '暂无词云数据',
    },
  },
  setup(props) {
    const svgRef = ref(null);

    /**
     * @description 根据词汇数组，计算词频，并转换成d3-cloud所需的格式
     * @param {Array<string>} rawWords 原始词汇数组
     * @returns {Array<{text: string, size: number}>} 转换后的数据
     */
    const prepareData = (rawWords) => {
      if (!rawWords || rawWords.length === 0) return [];

      // 1. 计算词频
      const wordCounts = rawWords.reduce((acc, word) => {
        acc[word] = (acc[word] || 0) + 1;
        return acc;
      }, {});

      // 2. 转换为d3-cloud所需的格式，并找出最大/最小词频
      let minCount = Infinity;
      let maxCount = -Infinity;
      const data = Object.entries(wordCounts).map(([text, count]) => {
        if (count < minCount) minCount = count;
        if (count > maxCount) maxCount = count;
        return { text, count };
      });

      // 3. 将词频映射到字号范围
      const sizeScale = d3
        .scaleLinear()
        .domain([minCount, maxCount])
        .range([props.minFontSize, props.maxFontSize]);

      return data.map((d) => ({
        ...d,
        size: sizeScale(d.count), // size就是字号
      }));
    };

    /**
     * @description 绘制词云
     * @param {Array<{text: string, size: number, x: number, y: number, rotate: number}>} words 布局后的词汇数据
     */
    const drawCloud = (words) => {
      const svg = d3.select(svgRef.value);
      // 清空SVG内容
      svg.selectAll('*').remove();

      // 创建一个<g>元素并平移到中心
      const g = svg
        .append('g')
        .attr(
          'transform',
          `translate(${props.width / 2}, ${props.height / 2})`
        );

      // 绑定数据并创建<text>元素
      const wordElements = g.selectAll('text').data(words, (d) => d.text);

      // 进入(Enter)阶段：添加新的<text>元素
      wordElements
        .enter()
        .append('text')
        .style('font-size', (d) => `${d.size}px`)
        .style('fill', (_, i) => props.colors[i % props.colors.length]) // 循环使用颜色
        .attr('text-anchor', 'middle') // 文本居中
        // *********** 关键修改：移除悬浮样式和交互，保持水平布局 ***********
        .attr('transform', (d) => `translate(${d.x}, ${d.y})`) // 移除rotate属性
        .text((d) => d.text);
        // ************************************************************

      // 退出(Exit)阶段：移除多余的<text>元素（如果更新时词汇减少）
      wordElements.exit().remove();
    };

    /**
     * @description 启动d3-cloud布局引擎
     * @param {Array<{text: string, count: number, size: number}>} data 待布局的词汇数据
     */
    const generateLayout = (data) => {
      if (!data || data.length === 0) {
        d3.select(svgRef.value).selectAll('*').remove();
        return;
      }

      const layout = cloud()
        .size([props.width, props.height]) // 词云的尺寸
        .words(data) // 传入词汇数据
        .padding(5) // 词汇之间的最小间距
        // *********** 关键修改：固定旋转角度为 0，实现水平布局 ***********
        .rotate(0)
        // ************************************************************
        .font('Impact') // 字体
        .fontSize((d) => d.size) // 使用计算出的字号
        .on('end', drawCloud); // 布局计算完成后调用drawCloud

      layout.start(); // 启动布局
    };

    /**
     * @description 核心渲染函数，处理数据并启动布局
     */
    const renderWordCloud = () => {
      // 保证在DOM更新后执行
      nextTick(() => {
        const processedData = prepareData(props.words);
        generateLayout(processedData);
      });
    };

    onMounted(renderWordCloud);

    // 监听props变化，重新渲染词云
    watch(() => [props.words, props.width, props.height, props.minFontSize, props.maxFontSize], () => {
      renderWordCloud();
    }, { deep: true });

    return {
      svgRef,
      renderWordCloud,
    };
  },
});
</script>

<style lang="scss">
.word-cloud-container {
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  width: v-bind(width + 'px'); // 动态绑定宽度
  height: v-bind(height + 'px'); // 动态绑定高度
  border: 1px solid #ebeef5;
  border-radius: 4px;
  overflow: hidden;

  // 词云SVG的样式
  svg {
    display: block; // 移除底部空白
  }

  // 词汇文本的通用样式
  text {
    font-weight: bold;
    // *********** 关键修改：移除transition和cursor，避免交互 ***********
  }

  .no-data-message {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 16px;
    color: #909399;
    background-color: #f5f7fa;
  }
}
</style>