# Conversations 会话列表组件

# 功能说明

会话列表组件,用于展示和管理多个对话会话,支持以下特性:

  • 会话列表展示和滚动加载
  • 分组显示和吸顶效果
  • 内置下拉菜单和自定义菜单
  • 自定义项目样式和容器样式
  • 加载更多和回到顶部功能
  • 自定义分组和项目内容

# 使用示例

# 基础用法

基本的会话列表组件使用,可以显示简单的会话列表并进行选中。

<template>
  <el-x-conversations
    :items="basicItems"
    :active="activeConversation"
    @change="handleConversationChange"
  />
</template>

<script>
  export default {
    data() {
      return {
        basicItems: [
          { id: '1', label: '最近对话1', prefixIcon: 'el-icon-chat-dot-round' },
          { id: '2', label: '最近对话2', prefixIcon: 'el-icon-chat-round' },
          {
            id: '3',
            label: '最近对话3',
            prefixIcon: 'el-icon-chat-line-round',
          },
          { id: '4', label: '最近对话4', prefixIcon: 'el-icon-chat-dot-round' },
          {
            id: '5',
            label: '最近对话5',
            prefixIcon: 'el-icon-chat-round',
            disabled: true,
          },
        ],
        activeConversation: '1',
      };
    },
    methods: {
      handleConversationChange(item) {
        this.activeConversation = item.uniqueKey;
      },
    },
  };
</script>
显示代码 复制代码

# 分组与吸顶效果

启用分组功能后,滚动时分组标题会自动吸顶显示。

<template>
  <div style="height: 300px; overflow: hidden; border: 1px solid #ebeef5; border-radius: 4px;">
    <el-x-conversations
      :items="groupedItems"
      :active="activeGroupedConversation"
      :groupable="true"
      @change="handleGroupedChange"
    />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        groupedItems: [
          {
            id: 'g1',
            label: '工作群1',
            group: '工作',
            prefixIcon: 'el-icon-office-building',
          },
          {
            id: 'g2',
            label: '工作群2',
            group: '工作',
            prefixIcon: 'el-icon-office-building',
          },
          {
            id: 'g3',
            label: '工作群3',
            group: '工作',
            prefixIcon: 'el-icon-office-building',
          },
          {
            id: 'g4',
            label: '学习小组1',
            group: '学习',
            prefixIcon: 'el-icon-reading',
          },
          {
            id: 'g5',
            label: '学习小组2',
            group: '学习',
            prefixIcon: 'el-icon-reading',
          },
          {
            id: 'g6',
            label: '同学聊天群',
            group: '学习',
            prefixIcon: 'el-icon-reading',
          },
          {
            id: 'g7',
            label: '家人群聊',
            group: '家庭',
            prefixIcon: 'el-icon-house',
          },
          {
            id: 'g8',
            label: '亲戚群',
            group: '家庭',
            prefixIcon: 'el-icon-house',
          },
          {
            id: 'g9',
            label: '朋友圈1',
            group: '朋友',
            prefixIcon: 'el-icon-user',
          },
          {
            id: 'g10',
            label: '朋友圈2',
            group: '朋友',
            prefixIcon: 'el-icon-user',
          },
          {
            id: 'g11',
            label: '朋友圈3',
            group: '朋友',
            prefixIcon: 'el-icon-user',
          },
          {
            id: 'g12',
            label: '未分类会话1',
            prefixIcon: 'el-icon-chat-line-round',
          },
          {
            id: 'g13',
            label: '未分类会话2',
            prefixIcon: 'el-icon-chat-line-round',
          },
        ],
        activeGroupedConversation: 'g1',
      };
    },
    methods: {
      handleGroupedChange(item) {
        this.activeGroupedConversation = item.uniqueKey;
      },
    },
  };
</script>
显示代码 复制代码

# 自定义分组排序

可以通过自定义排序函数控制分组的顺序,例如按字母排序或其他逻辑。

<template>
  <el-x-conversations
    :items="customGroupedItems"
    :groupable="groupSortOptions"
    :active="activeCustomGrouped"
    @change="handleCustomGroupedChange"
  />
</template>

<script>
  export default {
    data() {
      return {
        customGroupedItems: [
          {
            id: 'c1',
            label: '项目A讨论',
            group: 'A级项目',
            prefixIcon: 'el-icon-s-flag',
          },
          {
            id: 'c2',
            label: '项目B规划',
            group: 'B级项目',
            prefixIcon: 'el-icon-s-flag',
          },
          {
            id: 'c3',
            label: '项目C评审',
            group: 'C级项目',
            prefixIcon: 'el-icon-s-flag',
          },
          {
            id: 'c4',
            label: '项目A需求',
            group: 'A级项目',
            prefixIcon: 'el-icon-s-flag',
          },
          {
            id: 'c5',
            label: '项目B进度',
            group: 'B级项目',
            prefixIcon: 'el-icon-s-flag',
          },
          {
            id: 'c6',
            label: '项目D立项',
            group: 'D级项目',
            prefixIcon: 'el-icon-s-flag',
          },
        ],
        groupSortOptions: {
          sort: (a, b) => {
            // 按项目级别排序: A > B > C > D > 未分组
            const levels = { A级项目: 1, B级项目: 2, C级项目: 3, D级项目: 4 };
            return (levels[a] || 999) - (levels[b] || 999);
          },
        },
        activeCustomGrouped: 'c1',
      };
    },
    methods: {
      handleCustomGroupedChange(item) {
        this.activeCustomGrouped = item.uniqueKey;
      },
    },
  };
</script>
显示代码 复制代码

# 内置下拉菜单

启用内置菜单后,鼠标悬停在项目上会显示操作菜单,点击菜单项触发对应操作。

启用内置菜单
<template>
  <div>
    <div style="margin-bottom: 15px;">
      <el-switch
        v-model="showBuiltInMenu"
        active-text="启用内置菜单"
      />
    </div>
    <el-x-conversations
      :items="menuItems"
      :active="activeMenuItem"
      :show-built-in-menu="showBuiltInMenu"
      @change="handleMenuItemChange"
      @menu-command="handleMenuCommand"
    />
    <div
      v-if="operationLogs.length > 0"
      style="margin-top: 15px; padding: 10px; background-color: #f9f9f9; border-radius: 4px;"
    >
      <div style="margin-bottom: 8px; font-weight: 500; font-size: 14px;">操作记录:</div>
      <div
        v-for="(log, index) in operationLogs"
        :key="index"
        style="padding: 4px 0; border-bottom: 1px dashed #ebeef5; font-size: 13px;"
      >
        {{ log }}
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        menuItems: [
          { id: 'm1', label: '产品需求讨论', prefixIcon: 'el-icon-document' },
          { id: 'm2', label: '每周例会', prefixIcon: 'el-icon-date' },
          { id: 'm3', label: '营销策略', prefixIcon: 'el-icon-data-analysis' },
          { id: 'm4', label: '技术架构评审', prefixIcon: 'el-icon-cpu' },
        ],
        activeMenuItem: 'm1',
        showBuiltInMenu: true,
        operationLogs: [],
      };
    },
    methods: {
      handleMenuItemChange(item) {
        this.activeMenuItem = item.uniqueKey;
      },
      handleMenuCommand(command, item) {
        const actionMap = {
          rename: '重命名',
          delete: '删除',
        };
        const actionText = actionMap[command] || command;
        this.operationLogs.unshift(`${actionText} - ${item.label}`);

        // 最多显示5条记录
        if (this.operationLogs.length > 5) {
          this.operationLogs.pop();
        }
      },
    },
  };
</script>
显示代码 复制代码

# 自定义菜单交互

可以自定义菜单项的图标、文本和样式,并处理菜单命令事件。

<template>
  <el-x-conversations
    :items="customMenuItems"
    :active="activeCustomMenuItem"
    :show-built-in-menu="true"
    :menu="customMenu"
    @change="handleCustomMenuItemChange"
    @menu-command="handleCustomMenuCommand"
  />
</template>

<script>
  export default {
    data() {
      return {
        customMenuItems: [
          {
            id: 'cm1',
            label: '重要客户A',
            prefixIcon: 'el-icon-user',
            suffixIcon: 'el-icon-star-on',
          },
          { id: 'cm2', label: '潜在客户B', prefixIcon: 'el-icon-user' },
          { id: 'cm3', label: '合作伙伴C', prefixIcon: 'el-icon-user-solid' },
          { id: 'cm4', label: '供应商D', prefixIcon: 'el-icon-user' },
        ],
        customMenu: [
          {
            label: '标为重要',
            key: 'star',
            icon: 'el-icon-star-off',
            command: 'star',
            menuItemHoverStyle: {
              color: '#E6A23C',
              backgroundColor: 'rgba(230, 162, 60, 0.1)',
            },
          },
          {
            label: '归档',
            key: 'archive',
            icon: 'el-icon-folder',
            command: 'archive',
          },
          {
            label: '删除',
            key: 'delete',
            icon: 'el-icon-delete',
            command: 'delete',
            menuItemHoverStyle: {
              color: '#F56C6C',
              backgroundColor: 'rgba(245, 108, 108, 0.1)',
            },
          },
        ],
        activeCustomMenuItem: 'cm1',
      };
    },
    methods: {
      handleCustomMenuItemChange(item) {
        this.activeCustomMenuItem = item.uniqueKey;
      },
      handleCustomMenuCommand(command, item) {
        this.$message({
          message: `执行操作: ${command} - ${item.label}`,
          type: 'success',
        });

        // 如果是星标操作,切换星标状态
        if (command === 'star') {
          const targetItem = this.customMenuItems.find(i => i.id === item.id);
          if (targetItem) {
            targetItem.suffixIcon = targetItem.suffixIcon ? null : 'el-icon-star-on';
          }
        }
      },
    },
  };
</script>
显示代码 复制代码

# 加载更多功能

滚动到底部时自动加载更多项目,并支持回到顶部按钮。

按钮样式:

<template>
  <div>
    <div style="border: 1px solid #ebeef5; border-radius: 4px; height: 300px; overflow: hidden;">
      <el-x-conversations
        ref="lazyConversations"
        :items="lazyItems"
        :active="activeLazyItem"
        :load-more="loadMoreItems"
        :load-more-loading="isLoadingMore"
        :show-to-top-btn="true"
        :to-top-btn-type="toTopBtnType"
        @change="handleLazyItemChange"
      />
    </div>
    <div style="margin-top: 15px;">
      <div style="display: flex; align-items: center; margin-bottom: 15px;">
        <h4 style="margin: 0 10px 0 0; font-size: 14px; font-weight: normal; width: 80px;">
          按钮样式:
        </h4>
        <el-radio-group
          v-model="toTopBtnType"
          size="small"
        >
          <el-radio-button label="primary">主要</el-radio-button>
          <el-radio-button label="success">成功</el-radio-button>
          <el-radio-button label="warning">警告</el-radio-button>
          <el-radio-button label="danger">危险</el-radio-button>
        </el-radio-group>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        lazyItems: [],
        activeLazyItem: '',
        isLoadingMore: false,
        currentLazyPage: 0,
        maxLazyPages: 5,
        toTopBtnType: 'primary',
      };
    },
    created() {
      this.loadMoreItems();
    },
    methods: {
      // 防抖处理的加载更多方法
      loadMoreItems() {
        // 如果已经在加载中,直接返回
        if (this.isLoadingMore) return;

        console.log(`触底加载第${this.currentLazyPage + 1}页数据`);
        this.isLoadingMore = true;

        // 模拟异步加载
        setTimeout(() => {
          const newPage = this.currentLazyPage + 1;
          // 每次都加载10条
          const itemsCount = 10;
          const newItems = Array(itemsCount)
            .fill(0)
            .map((_, index) => {
              const itemId = `lazy${newPage}-${index + 1}`;
              return {
                id: itemId,
                label: `加载项目 ${newPage}-${index + 1}`,
                prefixIcon: 'el-icon-time',
              };
            });

          this.lazyItems = [...this.lazyItems, ...newItems];
          this.currentLazyPage = newPage;
          this.isLoadingMore = false;

          console.log(
            `${newPage}页加载完成,本次加载${newItems.length}条,总计${this.lazyItems.length}`,
          );

          // 如果是第一页,默认选中第一项
          if (newPage === 1 && newItems.length > 0) {
            this.activeLazyItem = newItems[0].id;
          }
        }, 2000);
      },
      handleLazyItemChange(item) {
        this.activeLazyItem = item.uniqueKey;
      },
    },
  };
</script>
显示代码 复制代码

# 自定义样式与分组标题

可以自定义项目样式、容器样式,以及通过插槽自定义分组标题和项目内容。

<template>
  <el-x-conversations
    :items="styledItems"
    :active="activeStyledItem"
    :groupable="true"
    :items-style="customItemsStyle"
    :items-hover-style="customItemsHoverStyle"
    :items-active-style="customItemsActiveStyle"
    :style-config="customContainerStyle"
    @change="handleStyledItemChange"
  >
    <template #group-title="{ group }">
      <div style="display: flex; align-items: center;">
        <i
          :class="getGroupIcon(group.title)"
          style="margin-right: 8px; color: #67C23A;"
        ></i>
        <span>{{ group.title }}</span>
        <span style="margin-left: 5px; font-size: 12px; color: #909399;">
          ({{ group.children.length }})
        </span>
      </div>
    </template>

    <template #label="{ item }">
      <div style="display: flex; align-items: center; width: 100%;">
        <i
          :class="item.icon || 'el-icon-chat-dot-round'"
          style="color: #409EFF; margin-right: 8px;"
        ></i>
        <span style="flex: 1;">{{ item.label }}</span>
        <span style="font-size: 12px; color: #909399;">{{ item.time }}</span>
      </div>
    </template>
  </el-x-conversations>
</template>

<script>
  export default {
    data() {
      return {
        styledItems: [
          {
            id: 's1',
            label: '设计讨论',
            group: '设计部',
            time: '10:30',
            icon: 'el-icon-picture-outline',
          },
          {
            id: 's2',
            label: 'UI评审',
            group: '设计部',
            time: '昨天',
            icon: 'el-icon-picture',
          },
          {
            id: 's3',
            label: '后端架构',
            group: '技术部',
            time: '周一',
            icon: 'el-icon-s-operation',
          },
          {
            id: 's4',
            label: 'API讨论',
            group: '技术部',
            time: '周二',
            icon: 'el-icon-s-platform',
          },
          {
            id: 's5',
            label: '产品规划',
            group: '产品部',
            time: '3天前',
            icon: 'el-icon-s-goods',
          },
          {
            id: 's6',
            label: '需求梳理',
            group: '产品部',
            time: '上周',
            icon: 'el-icon-document',
          },
        ],
        activeStyledItem: 's1',
        customItemsStyle: {
          borderRadius: '4px',
          margin: '4px 10px 4px 0',
          padding: '10px 12px',
        },
        customItemsHoverStyle: {
          backgroundColor: '#f0f9eb',
          boxShadow: '0 2px 4px rgba(0, 0, 0, 0.05)',
        },
        customItemsActiveStyle: {
          backgroundColor: '#f0f9eb',
          borderLeft: '3px solid #67C23A',
        },
        customContainerStyle: {
          borderRadius: '8px',
          boxShadow: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
          width: '320px',
        },
      };
    },
    methods: {
      handleStyledItemChange(item) {
        this.activeStyledItem = item.uniqueKey;
      },
      getGroupIcon(group) {
        const iconMap = {
          设计部: 'el-icon-picture-outline',
          技术部: 'el-icon-s-operation',
          产品部: 'el-icon-s-goods',
        };
        return iconMap[group] || 'el-icon-folder';
      },
    },
  };
</script>
显示代码 复制代码

# 属性

参数 说明 类型 默认值
items 会话列表数据 Array []
itemsStyle 项目自定义样式 Object {}
itemsHoverStyle 项目悬停样式 Object {}
itemsActiveStyle 项目选中样式 Object {}
itemsMenuOpenedStyle 项目菜单打开时样式 Object {}
styleConfig 容器样式配置 Object { padding: '10px 0 10px 10px', backgroundColor: '#fff', borderRadius: '8px', width: '280px', height: '0' }
showTooltip 是否显示提示框 Boolean false
groupable 是否启用分组,可传入排序函数 Boolean/Object false
labelMaxWidth 标签最大宽度 Number undefined
labelHeight 标签高度 Number 20
showBuiltInMenu 是否显示内置菜单 Boolean false
menu 自定义菜单项配置 Array [{ label: '重命名', key: 'rename', icon: 'el-icon-edit', command: 'rename' }, { label: '删除', key: 'delete', icon: 'el-icon-delete', command: 'delete', menuItemHoverStyle: { color: 'red', backgroundColor: 'rgba(255, 0, 0, 0.1)' } }]
ungroupedTitle 未分组项标题 String '未分组'
tooltipPlacement 提示框位置 String 'top'
tooltipOffset 提示框偏移量 Number 12
menuPlacement 菜单位置 String 'bottom-start'
menuShowArrow 菜单是否显示箭头 Boolean false
menuStyle 菜单自定义样式 Object {}
loadMoreLoading 加载更多状态 Boolean false
showToTopBtn 是否显示回到顶部按钮 Boolean false
toTopBtnType 回到顶部按钮类型 String 'primary'
toTopBtnStyle 回到顶部按钮自定义样式 Object {}
labelKey 项目标签字段名 String 'label'
rowKey 项目唯一键字段名 String 'id'
active 当前激活项 String/Number/Boolean ''
loadMore 加载更多函数 Function null

# 方法

方法名 说明 参数 返回值
scrollToTop 滚动到顶部 - -

# 事件

事件名 说明 回调参数
change 选择项改变事件 选中的项目对象
menuCommand 菜单命令事件 (command, item):命令名称和项目对象

# 插槽

插槽名 说明 插槽参数
header 列表顶部内容 -
footer 列表底部内容 -
group-title 分组标题 { group: 分组对象 }
label 项目标签内容 { item: 项目对象 }
menu 自定义菜单内容 { item: 项目对象 }
more-filled 更多图标内容 菜单相关属性
load-more 加载更多内容 -