# ConversationActions 组件自定义文档

## 概述

ConversationActions 是一个会话操作组件，为会话列表中的每个会话提供快捷操作功能。它支持置顶、静音、删除、标记未读等基础操作，同时允许添加自定义操作。组件会根据设备类型（桌面端/移动端）自动适配显示方式，在桌面端以弹出菜单形式展示，在移动端以模态框形式展示。适用于各种即时通讯场景，提升用户操作体验。

## Props 配置表格

| 字段 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| conversation | `ConversationModel` | - | 会话模型对象 |
| enablePin | `boolean` | `true` | 是否启用置顶功能 |
| enableMute | `boolean` | `true` | 是否启用静音功能 |
| enableDelete | `boolean` | `true` | 是否启用删除功能 |
| enableMarkUnread | `boolean` | `true` | 是否启用标记未读功能 |
| onMarkConversationUnread | `(conversation: ConversationModel, e?: React.MouseEvent) => void` | - | 标记未读回调函数 |
| onConversationPin | `(conversation: ConversationModel, e?: React.MouseEvent) => void` | - | 置顶操作回调函数 |
| onConversationMute | `(conversation: ConversationModel, e?: React.MouseEvent) => void` | - | 静音操作回调函数 |
| onConversationDelete | `(conversation: ConversationModel, e?: React.MouseEvent) => void` | - | 删除操作回调函数 |
| customConversationActions | `Record<string, ConversationActionItem>` | - | 自定义操作配置 |
| PopupIcon | `React.ReactElement` | `<IconHorizontalMoreTwo />` | 自定义弹出图标 |
| PopupElements | `React.ReactElement[]` | - | 自定义弹出元素数组 |
| onClick | `(e: React.MouseEvent, key?: string, conversation?: ConversationModel) => void` | - | 操作点击统一回调 |
| onClose | `() => void` | - | 关闭操作菜单回调（移动端） |
| className | `string` | - | 自定义样式类名 |
| style | `React.CSSProperties` | - | 自定义样式 |

## Props 详解

### conversation

type: `ConversationModel`

conversation 用于传入会话模型对象，包含会话的所有信息和操作方法，默认值为 `-`。

### enablePin

type: `boolean`

enablePin 用于控制是否显示置顶/取消置顶操作，默认值为 `true`。

### enableMute

type: `boolean`

enableMute 用于控制是否显示静音/取消静音操作，默认值为 `true`。

### enableDelete

type: `boolean`

enableDelete 用于控制是否显示删除会话操作，默认值为 `true`。

### enableMarkUnread

type: `boolean`

enableMarkUnread 用于控制是否显示标记未读/已读操作，默认值为 `true`。

### onMarkConversationUnread

type: `(conversation: ConversationModel, e?: React.MouseEvent) => void`

onMarkConversationUnread 用于自定义标记未读操作的处理逻辑，默认值为 `-`。

#### Example1: 基础标记未读处理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithMarkUnread({ conversation }: { conversation: ConversationModel }) {
  const handleMarkUnread = (conversation: ConversationModel, e?: React.MouseEvent) => {
    console.log('标记未读状态:', conversation.conversationID);
    
    // 发送统计数据
    analytics.track('conversation_mark_unread', {
      conversationId: conversation.conversationID,
      conversationType: conversation.type,
      timestamp: Date.now()
    });
    
    // 显示操作反馈
    if (conversation.unreadCount > 0) {
      showToast('已标记为已读');
    } else {
      showToast('已标记为未读');
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      onMarkConversationUnread={handleMarkUnread}
    />
  );
}
```

#### Example2: 权限控制的标记未读

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithPermissionCheck({ conversation }: { conversation: ConversationModel }) {
  const handleMarkUnread = async (conversation: ConversationModel, e?: React.MouseEvent) => {
    try {
      // 权限检查
      const hasPermission = await checkMarkUnreadPermission(conversation.conversationID);
      if (!hasPermission) {
        showErrorToast('您没有权限修改此会话的已读状态');
        return;
      }

      // 执行标记操作
      const isCurrentlyUnread = conversation.unreadCount > 0;
      await markConversationReadStatus(conversation.conversationID, !isCurrentlyUnread);
      
      // 更新本地状态
      updateConversationLocalState(conversation.conversationID, {
        unreadCount: isCurrentlyUnread ? 0 : 1
      });
      
      // 显示成功提示
      showSuccessToast(isCurrentlyUnread ? '已标记为已读' : '已标记为未读');
      
    } catch (error) {
      console.error('标记未读状态失败:', error);
      showErrorToast('操作失败，请重试');
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      onMarkConversationUnread={handleMarkUnread}
    />
  );
}
```

### onConversationPin

type: `(conversation: ConversationModel, e?: React.MouseEvent) => void`

onConversationPin 用于自定义置顶操作的处理逻辑，默认值为 `-`。

#### Example1: 基础置顶处理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithPin({ conversation }: { conversation: ConversationModel }) {
  const handlePin = (conversation: ConversationModel, e?: React.MouseEvent) => {
    const isPinning = !conversation.isPinned;
    console.log(isPinning ? '置顶会话:' : '取消置顶:', conversation.conversationID);
    
    // 发送统计数据
    analytics.track('conversation_pin', {
      conversationId: conversation.conversationID,
      action: isPinning ? 'pin' : 'unpin',
      timestamp: Date.now()
    });
    
    // 显示操作反馈
    showToast(isPinning ? '已置顶' : '已取消置顶');
  };

  return (
    <ConversationActions
      conversation={conversation}
      onConversationPin={handlePin}
    />
  );
}
```

#### Example2: 置顶数量限制处理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';
import { useState, useEffect } from 'react';

function ConversationActionsWithPinLimit({ conversation }: { conversation: ConversationModel }) {
  const [pinnedCount, setPinnedCount] = useState(0);
  const MAX_PINNED_CONVERSATIONS = 5; // 最大置顶数量

  useEffect(() => {
    // 获取当前置顶会话数量
    fetchPinnedConversationsCount().then(setPinnedCount);
  }, []);

  const handlePin = async (conversation: ConversationModel, e?: React.MouseEvent) => {
    const isPinning = !conversation.isPinned;
    
    try {
      if (isPinning && pinnedCount >= MAX_PINNED_CONVERSATIONS) {
        // 达到置顶上限，显示确认对话框
        const shouldProceed = await showConfirmDialog({
          title: '置顶会话已达上限',
          message: `最多只能置顶 ${MAX_PINNED_CONVERSATIONS} 个会话，是否取消最早的置顶会话？`,
          confirmText: '确认',
          cancelText: '取消'
        });
        
        if (!shouldProceed) {
          return;
        }
        
        // 取消最早的置顶会话
        await unpinOldestConversation();
      }
      
      // 执行置顶操作
      await conversation.pinConversation();
      
      // 更新置顶数量
      setPinnedCount(prev => isPinning ? prev + 1 : prev - 1);
      
      // 显示成功提示
      showSuccessToast(isPinning ? '置顶成功' : '已取消置顶');
      
    } catch (error) {
      console.error('置顶操作失败:', error);
      showErrorToast('操作失败，请重试');
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      onConversationPin={handlePin}
    />
  );
}
```

### onConversationMute

type: `(conversation: ConversationModel, e?: React.MouseEvent) => void`

onConversationMute 用于自定义静音操作的处理逻辑，默认值为 `-`。

#### Example1: 基础静音处理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithMute({ conversation }: { conversation: ConversationModel }) {
  const handleMute = (conversation: ConversationModel, e?: React.MouseEvent) => {
    const isMuting = !conversation.isMuted;
    console.log(isMuting ? '静音会话:' : '取消静音:', conversation.conversationID);
    
    // 发送统计数据
    analytics.track('conversation_mute', {
      conversationId: conversation.conversationID,
      action: isMuting ? 'mute' : 'unmute',
      conversationType: conversation.type,
      timestamp: Date.now()
    });
    
    // 显示操作反馈
    showToast(isMuting ? '已静音' : '已取消静音');
  };

  return (
    <ConversationActions
      conversation={conversation}
      onConversationMute={handleMute}
    />
  );
}
```

#### Example2: 定时静音处理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithTimedMute({ conversation }: { conversation: ConversationModel }) {
  const handleMute = async (conversation: ConversationModel, e?: React.MouseEvent) => {
    const isMuting = !conversation.isMuted;
    
    if (isMuting) {
      // 显示静音时长选择对话框
      const muteOptions = [
        { label: '1小时', value: 1 * 60 * 60 * 1000 },
        { label: '8小时', value: 8 * 60 * 60 * 1000 },
        { label: '1天', value: 24 * 60 * 60 * 1000 },
        { label: '永久', value: -1 }
      ];
      
      const selectedDuration = await showMuteOptionsDialog(muteOptions);
      if (selectedDuration === null) {
        return; // 用户取消
      }
      
      try {
        // 执行静音操作
        await conversation.muteConversation(selectedDuration);
        
        // 设置定时器（如果不是永久静音）
        if (selectedDuration > 0) {
          setTimeout(() => {
            // 自动取消静音
            conversation.muteConversation(0);
            showToast('静音时间已到，已自动取消静音');
          }, selectedDuration);
        }
        
        const durationText = selectedDuration === -1 ? '永久' : formatDuration(selectedDuration);
        showSuccessToast(`已静音 ${durationText}`);
        
      } catch (error) {
        console.error('静音操作失败:', error);
        showErrorToast('静音失败，请重试');
      }
    } else {
      // 取消静音
      try {
        await conversation.muteConversation(0);
        showSuccessToast('已取消静音');
      } catch (error) {
        console.error('取消静音失败:', error);
        showErrorToast('操作失败，请重试');
      }
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      onConversationMute={handleMute}
    />
  );
}
```

### onConversationDelete

type: `(conversation: ConversationModel, e?: React.MouseEvent) => void`

onConversationDelete 用于自定义删除会话操作的处理逻辑，默认值为 `-`。

#### Example1: 基础删除处理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithDelete({ conversation }: { conversation: ConversationModel }) {
  const handleDelete = async (conversation: ConversationModel, e?: React.MouseEvent) => {
    // 显示确认对话框
    const confirmed = await showConfirmDialog({
      title: '删除会话',
      message: '确定要删除这个会话吗？删除后聊天记录将无法恢复。',
      confirmText: '删除',
      cancelText: '取消',
      confirmButtonType: 'danger'
    });
    
    if (!confirmed) {
      return;
    }
    
    try {
      console.log('删除会话:', conversation.conversationID);
      
      // 执行删除操作
      await conversation.deleteConversation();
      
      // 发送统计数据
      analytics.track('conversation_delete', {
        conversationId: conversation.conversationID,
        conversationType: conversation.type,
        timestamp: Date.now()
      });
      
      // 显示成功提示
      showSuccessToast('会话已删除');
      
    } catch (error) {
      console.error('删除会话失败:', error);
      showErrorToast('删除失败，请重试');
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      onConversationDelete={handleDelete}
    />
  );
}
```

#### Example2: 分级删除权限处理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithDeletePermission({ conversation }: { conversation: ConversationModel }) {
  const handleDelete = async (conversation: ConversationModel, e?: React.MouseEvent) => {
    try {
      // 检查删除权限
      const deletePermission = await checkDeletePermission(conversation.conversationID);
      
      if (deletePermission.level === 'none') {
        showErrorToast('您没有权限删除此会话');
        return;
      }
      
      let confirmMessage = '';
      let warningLevel = 'normal';
      
      if (deletePermission.level === 'admin_only') {
        confirmMessage = '您是管理员，删除此会话将影响所有成员。确定要删除吗？';
        warningLevel = 'high';
      } else if (deletePermission.level === 'self_only') {
        confirmMessage = '删除会话只会从您的会话列表中移除，不会影响其他成员。确定删除吗？';
        warningLevel = 'low';
      } else {
        confirmMessage = '确定要删除这个会话吗？删除后聊天记录将无法恢复。';
        warningLevel = 'normal';
      }
      
      // 显示分级确认对话框
      const confirmed = await showConfirmDialog({
        title: '删除会话',
        message: confirmMessage,
        confirmText: '删除',
        cancelText: '取消',
        confirmButtonType: warningLevel === 'high' ? 'danger' : 'primary',
        showWarningIcon: warningLevel === 'high'
      });
      
      if (!confirmed) {
        return;
      }
      
      // 执行删除操作
      await conversation.deleteConversation(deletePermission.deleteType);
      
      // 记录操作日志
      logUserAction('conversation_delete', {
        conversationId: conversation.conversationID,
        deleteLevel: deletePermission.level,
        deleteType: deletePermission.deleteType
      });
      
      showSuccessToast('会话已删除');
      
    } catch (error) {
      console.error('删除会话失败:', error);
      showErrorToast('删除失败，请重试');
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      onConversationDelete={handleDelete}
    />
  );
}
```

### customConversationActions

type: `Record<string, ConversationActionItem>`

customConversationActions 用于添加自定义操作项，每个操作项包含启用状态、标签和点击处理函数，默认值为 `-`。

#### Example1: 基础自定义操作

```tsx
import { ConversationActions, ConversationModel, ConversationActionItem } from 'tuikit-atomicx-react';

function ConversationActionsWithCustomActions({ conversation }: { conversation: ConversationModel }) {
  const customActions: Record<string, ConversationActionItem> = {
    archive: {
      enable: true,
      label: '归档',
      onClick: (conversation: ConversationModel) => {
        console.log('归档会话:', conversation.conversationID);
        archiveConversation(conversation.conversationID);
        showToast('会话已归档');
      }
    },
    export: {
      enable: true,
      label: '导出聊天记录',
      onClick: async (conversation: ConversationModel) => {
        try {
          showLoadingToast('正在导出...');
          await exportChatHistory(conversation.conversationID);
          hideLoadingToast();
          showSuccessToast('导出成功');
        } catch (error) {
          hideLoadingToast();
          showErrorToast('导出失败');
        }
      }
    },
    copyId: {
      enable: true,
      label: '复制会话ID',
      onClick: (conversation: ConversationModel) => {
        copyToClipboard(conversation.conversationID);
        showToast('会话ID已复制');
      }
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      customConversationActions={customActions}
    />
  );
}
```

#### Example2: 动态自定义操作

```tsx
import { ConversationActions, ConversationModel, ConversationActionItem } from 'tuikit-atomicx-react';
import { useState, useEffect } from 'react';

function ConversationActionsWithDynamicActions({ conversation }: { conversation: ConversationModel }) {
  const [customActions, setCustomActions] = useState<Record<string, ConversationActionItem>>({});

  useEffect(() => {
    // 根据会话类型和用户权限动态生成操作
    const generateActions = async () => {
      const actions: Record<string, ConversationActionItem> = {};
      
      // 群聊特有操作
      if (conversation.type === 'GROUP') {
        const isGroupAdmin = await checkGroupAdminStatus(conversation.conversationID);
        
        if (isGroupAdmin) {
          actions.manageGroup = {
            enable: true,
            label: '群管理',
            onClick: (conversation: ConversationModel) => {
              openGroupManagementDialog(conversation.conversationID);
            }
          };
          
          actions.groupSettings = {
            enable: true,
            label: '群设置',
            onClick: (conversation: ConversationModel) => {
              openGroupSettingsDialog(conversation.conversationID);
            }
          };
        }
        
        actions.groupInfo = {
          enable: true,
          label: '群资料',
          onClick: (conversation: ConversationModel) => {
            showGroupInfoDialog(conversation.conversationID);
          }
        };
      }
      
      // 私聊特有操作
      if (conversation.type === 'C2C') {
        actions.userProfile = {
          enable: true,
          label: '查看资料',
          onClick: (conversation: ConversationModel) => {
            showUserProfileDialog(conversation.userProfile.userID);
          }
        };
        
        const isFriend = await checkFriendshipStatus(conversation.userProfile.userID);
        if (!isFriend) {
          actions.addFriend = {
            enable: true,
            label: '添加好友',
            onClick: async (conversation: ConversationModel) => {
              try {
                await sendFriendRequest(conversation.userProfile.userID);
                showSuccessToast('好友申请已发送');
              } catch (error) {
                showErrorToast('发送好友申请失败');
              }
            }
          };
        }
      }
      
      // 通用操作
      actions.createShortcut = {
        enable: true,
        label: '创建快捷方式',
        onClick: (conversation: ConversationModel) => {
          createConversationShortcut(conversation);
          showToast('快捷方式已创建');
        }
      };
      
      // 高级用户功能
      const isVipUser = await checkVipStatus();
      if (isVipUser) {
        actions.backup = {
          enable: true,
          label: '备份到云端',
          onClick: async (conversation: ConversationModel) => {
            try {
              showLoadingToast('正在备份...');
              await backupConversationToCloud(conversation.conversationID);
              hideLoadingToast();
              showSuccessToast('备份成功');
            } catch (error) {
              hideLoadingToast();
              showErrorToast('备份失败');
            }
          }
        };
      }
      
      setCustomActions(actions);
    };
    
    generateActions();
  }, [conversation]);

  return (
    <ConversationActions
      conversation={conversation}
      customConversationActions={customActions}
    />
  );
}
```

### PopupIcon

type: `React.ReactElement`

PopupIcon 用于自定义操作菜单的触发图标，默认值为 `<IconHorizontalMoreTwo />`。

#### Example1: 自定义操作图标

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';
import { Icon, IconSettings } from '@tencentcloud/uikit-base-component-react';

function ConversationActionsWithCustomIcon({ conversation }: { conversation: ConversationModel }) {
  const customIcon = <Icon><IconSettings /></Icon>;

  return (
    <ConversationActions
      conversation={conversation}
      PopupIcon={customIcon}
    />
  );
}
```

#### Example2: 动态操作图标

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';
import { Icon, IconMore, IconAlert, IconStar } from '@tencentcloud/uikit-base-component-react';

function ConversationActionsWithDynamicIcon({ conversation }: { conversation: ConversationModel }) {
  // 根据会话状态选择不同图标
  const getIcon = () => {
    if (conversation.unreadCount > 10) {
      return <Icon style={{ color: '#ff4d4f' }}><IconAlert /></Icon>;
    } else if (conversation.isPinned) {
      return <Icon style={{ color: '#faad14' }}><IconStar /></Icon>;
    } else {
      return <Icon><IconMore /></Icon>;
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      PopupIcon={getIcon()}
    />
  );
}
```

### onClick

type: `(e: React.MouseEvent, key?: string, conversation?: ConversationModel) => void`

onClick 用于统一处理所有操作的点击事件，可以用于统计、日志记录等，默认值为 `-`。

#### Example1: 操作统计处理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithAnalytics({ conversation }: { conversation: ConversationModel }) {
  const handleActionClick = (e: React.MouseEvent, key?: string, conversation?: ConversationModel) => {
    // 统计操作使用情况
    analytics.track('conversation_action_click', {
      action: key,
      conversationId: conversation?.conversationID,
      conversationType: conversation?.type,
      timestamp: Date.now(),
      userAgent: navigator.userAgent
    });
    
    // 记录用户行为日志
    console.log(`用户执行操作: ${key} on ${conversation?.conversationID}`);
  };

  return (
    <ConversationActions
      conversation={conversation}
      onClick={handleActionClick}
    />
  );
}
```

#### Example2: 操作权限和审计

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithAudit({ conversation }: { conversation: ConversationModel }) {
  const handleActionClick = async (e: React.MouseEvent, key?: string, conversation?: ConversationModel) => {
    try {
      // 记录操作审计日志
      await logAuditEvent({
        userId: getCurrentUserId(),
        action: key,
        targetType: 'conversation',
        targetId: conversation?.conversationID,
        timestamp: new Date().toISOString(),
        clientInfo: {
          userAgent: navigator.userAgent,
          ip: await getClientIP(),
          sessionId: getSessionId()
        }
      });
      
      // 检查敏感操作
      const sensitiveActions = ['delete', 'mute', 'pin'];
      if (key && sensitiveActions.includes(key)) {
        // 发送实时通知给管理员
        await notifyAdmins({
          type: 'sensitive_action',
          userId: getCurrentUserId(),
          action: key,
          conversationId: conversation?.conversationID
        });
      }
      
      // 更新用户操作统计
      await updateUserActionStats(getCurrentUserId(), key);
      
    } catch (error) {
      console.error('记录操作审计失败:', error);
      // 审计失败不应该影响正常操作
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      onClick={handleActionClick}
    />
  );
}
```

### onClose

type: `() => void`

onClose 用于处理操作菜单关闭事件（主要用于移动端），默认值为 `-`。

#### Example1: 基础关闭处理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';

function ConversationActionsWithCloseHandler({ conversation }: { conversation: ConversationModel }) {
  const handleClose = () => {
    console.log('操作菜单已关闭');
    
    // 记录菜单使用时长
    const menuOpenTime = getMenuOpenTime();
    if (menuOpenTime) {
      const duration = Date.now() - menuOpenTime;
      analytics.track('conversation_actions_menu_usage', {
        conversationId: conversation.conversationID,
        duration,
        timestamp: Date.now()
      });
    }
  };

  return (
    <ConversationActions
      conversation={conversation}
      onClose={handleClose}
    />
  );
}
```

#### Example2: 关闭时状态清理

```tsx
import { ConversationActions, ConversationModel } from 'tuikit-atomicx-react';
import { useEffect, useState } from 'react';

function ConversationActionsWithStateCleanup({ conversation }: { conversation: ConversationModel }) {
  const [isMenuOpen, setIsMenuOpen] = useState(false);
  const [menuInteractions, setMenuInteractions] = useState<string[]>([]);

  const handleClose = () => {
    console.log('操作菜单关闭，清理状态');
    
    // 记录菜单交互数据
    if (menuInteractions.length > 0) {
      analytics.track('conversation_menu_interactions', {
        conversationId: conversation.conversationID,
        interactions: menuInteractions,
        interactionCount: menuInteractions.length,
        timestamp: Date.now()
      });
    }
    
    // 清理状态
    setIsMenuOpen(false);
    setMenuInteractions([]);
    
    // 清理全局状态
    clearMenuRelatedState();
    
    // 恢复页面滚动（移动端）
    document.body.style.overflow = 'auto';
  };

  useEffect(() => {
    // 菜单打开时的处理
    if (isMenuOpen) {
      // 禁止页面滚动（移动端）
      document.body.style.overflow = 'hidden';
    }
    
    return () => {
      // 组件卸载时恢复
      document.body.style.overflow = 'auto';
    };
  }, [isMenuOpen]);

  return (
    <ConversationActions
      conversation={conversation}
      onClose={handleClose}
    />
  );
}
``` 