# BubbleList 消息列表组件

# 功能说明

消息列表组件,用于展示一组对话消息,支持以下特性:

  • 自动滚动到最新消息
  • 返回底部按钮
  • 统一设置气泡样式
  • 自定义控制滚动
  • 自定义头像和内容渲染

# 使用示例

# 基础用法

基本的消息列表组件使用,展示多条消息。

<template>
  <div>
    <client-only>
      <el-x-bubble-list :list="messageList" />
    </client-only>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        messageList: [
          {
            content: '你好,我是AI助手,有什么可以帮助你的?',
            placement: 'start',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
            avatarSize: 40,
          },
          {
            content: '我想了解如何使用Element UI组件库',
            placement: 'end',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/tft_set10_emblem_pbj.png',
            avatarSize: 40,
          },
          {
            content:
              'Element UI是一套为开发者、设计师和产品经理准备的基于Vue 2.0的桌面端组件库,提供了丰富的组件和功能。',
            placement: 'start',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
            avatarSize: 40,
          },
        ],
      };
    },
  };
</script>
显示代码 复制代码

# 打字器效果和自动滚动

展示打字器效果和自动滚动到最新消息的功能。

<template>
  <div>
    <client-only>
      <el-x-bubble-list
        :list="typingMessages"
        :max-height="'300px'"
        @complete="onTypingComplete"
      />

      <div style="margin-top: 15px;">
        <el-button
          type="primary"
          size="small"
          @click="addMessage"
        >
          添加消息
        </el-button>
        <el-button
          type="success"
          size="small"
          @click="addTypingMessage"
        >
          添加打字效果消息
        </el-button>
      </div>
    </client-only>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        typingMessages: [
          {
            content: '这是一条普通消息',
            placement: 'start',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
            avatarSize: 40,
            isMarkdown: true,
          },
          {
            content: '这是我的回复',
            placement: 'end',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/tft_set10_emblem_pbj.png',
            avatarSize: 40,
            isMarkdown: true,
          },
          {
            content:
              '这是一条**带有打字效果**的消息,会逐字显示出来。\n\n当消息很长时,会自动滚动到底部。',
            placement: 'start',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
            typing: { interval: 30, step: 2 },
            avatarSize: 40,
            isMarkdown: true,
          },
        ],
        messageCounter: 0,
      };
    },
    methods: {
      addMessage() {
        this.messageCounter++;
        this.typingMessages.push({
          content: `这是新添加的第${this.messageCounter}条普通消息`,
          placement: this.messageCounter % 2 === 0 ? 'end' : 'start',
          avatarSize: 40,
          isMarkdown: true,
          avatar:
            this.messageCounter % 2 === 0
              ? 'https://game.gtimg.cn/images/lol/act/img/tft/equip/tft_set10_emblem_pbj.png'
              : 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
        });
      },
      addTypingMessage() {
        this.messageCounter++;
        this.typingMessages.push({
          content: `这是新添加的第${this.messageCounter}条**打字效果**消息,会自动滚动到底部。\n\n- 项目1\n- 项目2\n- 项目3`,
          placement: 'start',
          avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
          typing: { interval: 30, step: 2 },
          avatarSize: 40,
          isMarkdown: true,
        });
      },
      onTypingComplete(instance, index) {
        console.log(`索引 ${index} 的消息完成打字`);
      },
    },
  };
</script>
显示代码 复制代码

# 返回底部按钮

当滚动到顶部时,显示返回底部按钮。

<template>
  <div>
    <client-only>
      <el-x-bubble-list
        :list="longMessages"
        :max-height="'300px'"
        :back-button-threshold="80"
        :show-back-button="true"
        :btn-color="'#409EFF'"
        :btn-icon-size="24"
      />
    </client-only>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        longMessages: Array.from({ length: 20 }, (_, i) => ({
          content: `这是第 ${
            i + 1
          } 条消息,向上滚动查看更多内容,滚动距离超过阈值时会显示返回底部按钮。${
            i % 3 === 0 ? '这是一条较长的消息内容,用于测试换行和布局效果。' : ''
          }`,
          placement: i % 2 === 0 ? 'start' : 'end',
          avatarSize: 40,
          avatar:
            i % 2 === 0
              ? 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png'
              : 'https://game.gtimg.cn/images/lol/act/img/tft/equip/tft_set10_emblem_pbj.png',
        })),
      };
    },
  };
</script>
显示代码 复制代码

# 自定义插槽

使用插槽自定义气泡的头像和内容。

<template>
  <div>
    <client-only>
      <el-x-bubble-list
        :list="customMessages"
        :max-height="'300px'"
      >
        <template #avatar="{ item }">
          <el-avatar
            :size="40"
            :icon="
              item.placement === 'start' ? 'el-icon-s-custom' : 'el-icon-user'
            "
            :style="{
              backgroundColor: item.placement === 'start' ? '#409EFF' : '#67C23A',
            }"
          ></el-avatar>
        </template>

        <template #header="{ item }">
          <div style="font-weight: bold; margin-bottom: 5px; color: #606266;">
            {{ item.placement === "start" ? "AI助手" : "用户" }}
            <span style="font-size: 12px; margin-left: 10px; font-weight: normal;">
              {{ item.time }}
            </span>
          </div>
        </template>

        <template #footer="{ item }">
          <div style=" color: #909399; text-align: right; ">
            <el-button
              circle
              size="mini"
              @click="copyMessage(item)"
            >
              <i class="el-icon-document-copy"></i>
            </el-button>
            <el-button
              circle
              size="mini"
              @click="favoriteMessage(item)"
            >
              <i class="el-icon-star-off"></i>
            </el-button>

            <!-- 只在AI消息中显示点赞和踩按钮 -->
            <template v-if="item.placement === 'start'">
              <el-button
                circle
                size="mini"
                @click="likeMessage(item)"
              >
                <i class="el-icon-thumb"></i>
              </el-button>
              <el-button
                circle
                size="mini"
                @click="dislikeMessage(item)"
              >
                <i class="el-icon-bottom"></i>
              </el-button>
            </template>
          </div>
        </template>

        <template #backToBottom>
          <el-button
            type="primary"
            size="mini"
            circle
          >
            <i class="el-icon-bottom"></i>
          </el-button>
        </template>
      </el-x-bubble-list>
    </client-only>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        customMessages: Array.from({ length: 5 }, (_, i) => ({
          content: `这是第 ${i + 1} 条自定义消息,使用插槽自定义了头像、头部和底部内容。`,
          placement: i % 2 === 0 ? 'start' : 'end',
          time: new Date().toLocaleTimeString(),
          id: i,
        })),
      };
    },
    methods: {
      copyMessage(item) {
        this.$message.success(`已复制消息: ${item.id}`);
      },
      favoriteMessage(item) {
        this.$message.success(`已收藏消息: ${item.id}`);
      },
      likeMessage(item) {
        this.$message.success(`已点赞消息: ${item.id}`);
      },
      dislikeMessage(item) {
        this.$message.warning(`已踩消息: ${item.id}`);
      },
    },
  };
</script>
显示代码 复制代码

# 加载状态和复杂消息

展示加载状态和不同类型的消息内容。

<template>
  <div>
    <client-only>
      <el-x-bubble-list
        :list="complexMessages"
        :max-height="'400px'"
      />

      <div style="margin-top: 15px;">
        <el-button
          type="primary"
          size="small"
          @click="sendMessage"
        >
          发送消息
        </el-button>
      </div>
    </client-only>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        complexMessages: [
          {
            content: '你好,请问有什么可以帮助你的?',
            placement: 'start',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
            avatarSize: 40,
          },
          {
            content: '我想了解Element UI的表格组件',
            placement: 'end',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/tft_set10_emblem_pbj.png',
            avatarSize: 40,
          },
          {
            content: `# Element UI 表格组件

Element UI 表格组件是一个强大的数据展示工具,提供了以下功能:

- 支持多选
- 排序和筛选
- 固定表头和列
- 自定义单元格内容

\`\`\`javascript
<el-table :data="tableData">
  <el-table-column prop="date" label="日期" width="180"></el-table-column>
  <el-table-column prop="name" label="姓名" width="180"></el-table-column>
  <el-table-column prop="address" label="地址"></el-table-column>
</el-table>
\`\`\``,
            placement: 'start',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
            isMarkdown: true,
            avatarSize: 40,
          },
          {
            loading: true,
            placement: 'start',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
            avatarSize: 40,
          },
        ],
      };
    },
    methods: {
      sendMessage() {
        // 移除加载消息
        this.complexMessages.pop();

        // 添加用户消息
        this.complexMessages.push({
          content: '谢谢,这个表格组件看起来很强大!',
          placement: 'end',
          avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/tft_set10_emblem_pbj.png',
          avatarSize: 40,
        });

        // 添加加载中状态
        this.complexMessages.push({
          loading: true,
          placement: 'start',
          avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
          avatarSize: 40,
        });

        // 模拟加载完成后添加新消息
        setTimeout(() => {
          // 移除加载消息
          this.complexMessages.pop();

          // 添加新消息
          this.complexMessages.push({
            content: '你还有其他关于Element UI的问题吗?',
            placement: 'start',
            avatar: 'https://game.gtimg.cn/images/lol/act/img/tft/equip/HandofJustice.png',
            typing: true,
            avatarSize: 40,
          });
        }, 2000);
      },
    },
  };
</script>
显示代码 复制代码

# 属性

参数 说明 类型 默认值
list 气泡列表数据 Array []
maxHeight 列表最大高度 String '500px'
triggerIndices 触发气泡完成事件的索引,可选值:'only-last'/'all'/Array String/Array 'only-last'
alwaysShowScrollbar 是否始终显示滚动条 Boolean false
backButtonThreshold 显示回到底部按钮的滚动阈值 Number 80
showBackButton 是否显示回到底部按钮 Boolean true
backButtonPosition 回到底部按钮位置 Object { bottom: '20px', left: 'calc(50% - 19px)' }
btnLoading 按钮是否显示加载动画 Boolean true
btnColor 按钮颜色 String '#409EFF'
btnIconSize 按钮图标大小 Number 24

# 方法

方法名 说明 参数 返回值
scrollToTop 滚动到顶部 - -
scrollToBottom 滚动到底部 - -
scrollToBubble 滚动到指定的气泡 index: 气泡索引 -

# 事件

事件名 说明 回调参数
complete 气泡打字完成事件 (instance: 气泡实例, index: 气泡索引)

# 插槽

插槽名 说明
avatar 自定义头像内容,作用域参数: { item: 气泡数据项, index: 索引 }
header 自定义气泡头部内容,作用域参数: { item: 气泡数据项, index: 索引 }
content 自定义气泡主体内容,作用域参数: { item: 气泡数据项, index: 索引 }
footer 自定义气泡底部内容,作用域参数: { item: 气泡数据项, index: 索引 }
loading 自定义加载状态内容,作用域参数: { item: 气泡数据项, index: 索引 }
backToBottom 自定义返回底部按钮内容