# ConversationPreview 组件自定义文档

## 概述

ConversationPreview 是一个会话预览组件，用于在会话列表中展示单个会话的详细信息。组件支持显示会话头像、标题、最后一条消息摘要、时间戳、未读消息数量等信息，同时集成了会话操作功能（如置顶、静音、删除等）。支持桌面端鼠标悬停和移动端长按触发操作菜单，提供了丰富的自定义选项来满足不同的业务需求。

## Props 配置表格

| 字段 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| conversation | `ConversationModel` | - | 要显示的会话对象 |
| isSelected | `boolean` | `false` | 是否为当前选中的会话 |
| enableActions | `boolean` | `true` | 是否启用会话操作功能 |
| highlightMatchString | `string` | - | 在标题中高亮显示的匹配字符串 |
| Preview | `React.ComponentType<ConversationPreviewUIProps>` | `ConversationPreviewUI` | 自定义预览组件 |
| Avatar | `React.ComponentType<AvatarProps>` | `DefaultAvatar` | 自定义头像组件 |
| ConversationActions | `React.ComponentType<ConversationActionsProps>` | `DefaultConversationActions` | 自定义会话操作组件 |
| Title | `string \| JSX.Element` | - | 自定义标题组件 |
| LastMessageTimestamp | `string \| JSX.Element` | - | 自定义最后消息时间戳组件 |
| LastMessageAbstract | `string \| JSX.Element` | - | 自定义最后消息摘要组件 |
| Unread | `string \| JSX.Element` | - | 自定义未读消息显示组件 |
| onSelectConversation | `(conversation: ConversationModel) => void` | - | 点击会话时的回调函数 |
| actionsConfig | `ConversationActionsConfig` | - | 会话操作配置 |
| className | `string` | - | 自定义样式类名 |
| style | `React.CSSProperties` | - | 自定义样式 |
| children | `React.ReactNode` | - | 自定义子元素，完全替换默认UI |

## Props 详解

### conversation

type: `ConversationModel`

conversation 用于指定要显示的会话对象，包含会话的所有基本信息，默认值为 `-`。

### isSelected

type: `boolean`

isSelected 用于指示当前会话是否为选中状态，会影响组件的视觉样式，默认值为 `false`。

### enableActions

type: `boolean`

enableActions 用于控制是否启用会话操作功能（如置顶、静音、删除等），默认值为 `true`。

### highlightMatchString

type: `string`

highlightMatchString 用于在会话标题中高亮显示匹配的搜索字符串，默认值为 `-`。

### Preview

type: `React.ComponentType<ConversationPreviewUIProps>`

Preview 用于自定义整个会话预览的UI组件，可以完全替换默认的预览界面，默认值为 `ConversationPreviewUI`。

#### Example1: 卡片式会话预览

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';
import type { ConversationPreviewUIProps, ConversationModel } from 'tuikit-atomicx-react';
import { useState } from 'react';

function CardConversationPreview(props: ConversationPreviewUIProps) {
  const {
    conversation,
    isSelected,
    onSelectConversation,
    Avatar,
    className,
    style
  } = props;

  const handleClick = () => {
    onSelectConversation?.(conversation);
  };

  return (
    <div 
      className={`card-preview ${isSelected ? 'selected' : ''} ${className || ''}`}
      style={{
        border: '1px solid #e0e0e0',
        borderRadius: '12px',
        padding: '16px',
        margin: '8px 0',
        backgroundColor: isSelected ? '#f0f8ff' : '#fff',
        cursor: 'pointer',
        boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
        transition: 'all 0.2s ease',
        ...style
      }}
      onClick={handleClick}
    >
      <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
        {/* 头像区域 */}
        <div style={{ position: 'relative' }}>
          <Avatar 
            src={conversation.getAvatar()} 
            size="large"
            alt={conversation.getShowName()}
          />
          {/* 在线状态指示器 */}
          <div 
            style={{
              position: 'absolute',
              bottom: '2px',
              right: '2px',
              width: '12px',
              height: '12px',
              borderRadius: '50%',
              backgroundColor: conversation.isOnline ? '#4CAF50' : '#9E9E9E',
              border: '2px solid white'
            }}
          />
        </div>
        
        {/* 内容区域 */}
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ 
            display: 'flex', 
            justifyContent: 'space-between', 
            alignItems: 'center',
            marginBottom: '4px' 
          }}>
            <h3 style={{ 
              margin: 0, 
              fontSize: '16px', 
              fontWeight: '600',
              color: '#333',
              overflow: 'hidden',
              textOverflow: 'ellipsis',
              whiteSpace: 'nowrap'
            }}>
              {conversation.getShowName()}
            </h3>
            
            {/* 时间戳 */}
            <span style={{ 
              fontSize: '12px', 
              color: '#999',
              flexShrink: 0 
            }}>
              {formatTime(conversation.lastMessage?.lastTime)}
            </span>
          </div>
          
          {/* 最后消息 */}
          <p style={{ 
            margin: 0, 
            fontSize: '14px', 
            color: '#666',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap'
          }}>
            {getLastMessageText(conversation)}
          </p>
        </div>
        
        {/* 未读消息徽章 */}
        {conversation.unreadCount > 0 && (
          <div style={{
            backgroundColor: '#FF4444',
            color: 'white',
            borderRadius: '10px',
            padding: '2px 8px',
            fontSize: '12px',
            fontWeight: 'bold',
            minWidth: '20px',
            textAlign: 'center'
          }}>
            {conversation.unreadCount > 99 ? '99+' : conversation.unreadCount}
          </div>
        )}
      </div>
    </div>
  );
}

function ConversationListWithCardPreview() {
  const [selectedConversation, setSelectedConversation] = useState<ConversationModel | null>(null);

  const handleSelectConversation = (conversation: ConversationModel) => {
    setSelectedConversation(conversation);
    // 处理会话选择逻辑
    console.log('选中会话:', conversation.getShowName());
  };

  return (
    <div>
      {conversationList.map(conversation => (
        <ConversationPreview
          key={conversation.conversationID}
          conversation={conversation}
          isSelected={selectedConversation?.conversationID === conversation.conversationID}
          Preview={CardConversationPreview}
          onSelectConversation={handleSelectConversation}
        />
      ))}
    </div>
  );
}
```

#### Example2: 紧凑型会话预览

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';
import type { ConversationPreviewUIProps } from 'tuikit-atomicx-react';

function CompactConversationPreview(props: ConversationPreviewUIProps) {
  const {
    conversation,
    isSelected,
    onSelectConversation,
    Avatar,
    enableActions,
    ConversationActions,
    actionsConfig
  } = props;

  const [showActions, setShowActions] = useState(false);

  return (
    <div 
      className={`compact-preview ${isSelected ? 'selected' : ''}`}
      style={{
        display: 'flex',
        alignItems: 'center',
        padding: '8px 12px',
        backgroundColor: isSelected ? '#e3f2fd' : 'transparent',
        borderLeft: isSelected ? '3px solid #2196F3' : '3px solid transparent',
        cursor: 'pointer',
        transition: 'all 0.2s ease'
      }}
      onClick={() => onSelectConversation?.(conversation)}
      onMouseEnter={() => setShowActions(true)}
      onMouseLeave={() => setShowActions(false)}
    >
      {/* 简化头像 */}
      <Avatar 
        src={conversation.getAvatar()} 
        size="small"
        style={{ marginRight: '12px' }}
      />
      
      {/* 会话信息 */}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ 
          display: 'flex', 
          justifyContent: 'space-between',
          alignItems: 'center'
        }}>
          <span style={{ 
            fontSize: '14px', 
            fontWeight: isSelected ? '600' : '400',
            color: '#333',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
            maxWidth: '150px'
          }}>
            {conversation.getShowName()}
          </span>
          
          {/* 状态指示器 */}
          <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
            {conversation.isPinned && (
              <span style={{ fontSize: '12px', color: '#FF9800' }}>📌</span>
            )}
            {conversation.isMuted && (
              <span style={{ fontSize: '12px', color: '#9E9E9E' }}>🔇</span>
            )}
            {conversation.unreadCount > 0 && (
              <div style={{
                backgroundColor: '#F44336',
                color: 'white',
                borderRadius: '8px',
                padding: '1px 6px',
                fontSize: '10px',
                minWidth: '16px',
                textAlign: 'center'
              }}>
                {conversation.unreadCount > 9 ? '9+' : conversation.unreadCount}
              </div>
            )}
          </div>
        </div>
      </div>
      
      {/* 操作按钮 */}
      {enableActions && showActions && (
        <ConversationActions
          conversation={conversation}
          {...actionsConfig}
          onClose={() => setShowActions(false)}
        />
      )}
    </div>
  );
}

function CompactConversationList() {
  return (
    <div style={{ width: '280px', backgroundColor: '#fafafa' }}>
      {conversationList.map(conversation => (
        <ConversationPreview
          key={conversation.conversationID}
          conversation={conversation}
          Preview={CompactConversationPreview}
          enableActions={true}
        />
      ))}
    </div>
  );
} 

### Avatar

type: `React.ComponentType<AvatarProps>`

Avatar 用于自定义会话头像组件，可以替换默认的头像显示方式，默认值为 `DefaultAvatar`。

#### Example1: 带状态指示的头像

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';
import type { AvatarProps } from 'tuikit-atomicx-react';

function StatusAvatar(props: AvatarProps) {
  const { src, alt, size = 'medium', unreadCount, style, ...restProps } = props;
  
  const sizeMap = {
    small: 32,
    medium: 40,
    large: 48,
    big: 56
  };
  
  const avatarSize = typeof size === 'string' ? sizeMap[size] : size;

  return (
    <div 
      style={{ 
        position: 'relative', 
        display: 'inline-block',
        ...style 
      }}
      {...restProps}
    >
      {/* 头像主体 */}
      <img
        src={src || '/default-avatar.png'}
        alt={alt || '头像'}
        style={{
          width: avatarSize,
          height: avatarSize,
          borderRadius: '50%',
          objectFit: 'cover',
          border: '2px solid #e0e0e0'
        }}
      />
      
      {/* 在线状态指示器 */}
      <div
        style={{
          position: 'absolute',
          bottom: '2px',
          right: '2px',
          width: avatarSize * 0.25,
          height: avatarSize * 0.25,
          borderRadius: '50%',
          backgroundColor: '#4CAF50',
          border: '2px solid white',
          boxShadow: '0 1px 3px rgba(0,0,0,0.2)'
        }}
      />
      
      {/* 未读消息徽章 */}
      {unreadCount && (
        <div
          style={{
            position: 'absolute',
            top: '-4px',
            right: '-4px',
            backgroundColor: '#FF4444',
            color: 'white',
            borderRadius: '10px',
            padding: '2px 6px',
            fontSize: '10px',
            fontWeight: 'bold',
            minWidth: '16px',
            textAlign: 'center',
            boxShadow: '0 1px 3px rgba(0,0,0,0.3)'
          }}
        >
          {typeof unreadCount === 'number' ? (unreadCount > 99 ? '99+' : unreadCount) : ''}
        </div>
      )}
    </div>
  );
}

function ConversationWithStatusAvatar() {
  return (
    <ConversationPreview
      conversation={conversation}
      Avatar={StatusAvatar}
      onSelectConversation={(conv) => {
        console.log('选中会话:', conv.getShowName());
      }}
    />
  );
}
```

#### Example2: 群组头像拼接

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';
import type { AvatarProps, ConversationModel } from 'tuikit-atomicx-react';

function GroupAvatar(props: AvatarProps & { conversation?: ConversationModel }) {
  const { conversation, size = 'medium', style, ...restProps } = props;
  
  const sizeMap = {
    small: 32,
    medium: 40,
    large: 48,
    big: 56
  };
  
  const avatarSize = typeof size === 'string' ? sizeMap[size] : size;

  // 如果是群组，显示成员头像拼接
  if (conversation?.type === 'GROUP') {
    const memberAvatars = conversation.memberList?.slice(0, 4) || [];
    
    if (memberAvatars.length <= 1) {
      // 单个头像或使用群头像
      return (
        <img
          src={conversation.getAvatar() || '/default-group-avatar.png'}
          alt={conversation.getShowName()}
          style={{
            width: avatarSize,
            height: avatarSize,
            borderRadius: '12px',
            objectFit: 'cover',
            ...style
          }}
          {...restProps}
        />
      );
    }

    // 多个头像拼接
    const itemSize = avatarSize / 2 - 1;
    return (
      <div
        style={{
          width: avatarSize,
          height: avatarSize,
          borderRadius: '12px',
          overflow: 'hidden',
          display: 'grid',
          gridTemplateColumns: '1fr 1fr',
          gridTemplateRows: '1fr 1fr',
          gap: '2px',
          backgroundColor: '#f0f0f0',
          ...style
        }}
        {...restProps}
      >
        {memberAvatars.map((member, index) => (
          <img
            key={member.userID || index}
            src={member.avatar || '/default-avatar.png'}
            alt={member.nick || member.userID}
            style={{
              width: itemSize,
              height: itemSize,
              objectFit: 'cover'
            }}
          />
        ))}
      </div>
    );
  }

  // 普通单聊头像
  return (
    <img
      src={conversation?.getAvatar() || props.src || '/default-avatar.png'}
      alt={conversation?.getShowName() || props.alt || '头像'}
      style={{
        width: avatarSize,
        height: avatarSize,
        borderRadius: '50%',
        objectFit: 'cover',
        ...style
      }}
      {...restProps}
    />
  );
}

function ConversationWithGroupAvatar() {
  return (
    <ConversationPreview
      conversation={conversation}
      Avatar={(props) => <GroupAvatar {...props} conversation={conversation} />}
    />
  );
}
```

### Title

type: `string | JSX.Element`

Title 用于自定义会话标题的显示内容，可以是简单的字符串或复杂的JSX元素，默认值为 `-`。

#### Example1: 带徽章的标题

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';

function ConversationWithBadgeTitle() {
  const createTitleWithBadge = (conversation: ConversationModel) => {
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
        <span style={{ 
          fontSize: '16px', 
          fontWeight: '500',
          color: '#333',
          overflow: 'hidden',
          textOverflow: 'ellipsis',
          whiteSpace: 'nowrap'
        }}>
          {conversation.getShowName()}
        </span>
        
        {/* VIP 徽章 */}
        {conversation.isVIP && (
          <span style={{
            backgroundColor: '#FFD700',
            color: '#B8860B',
            fontSize: '10px',
            padding: '2px 6px',
            borderRadius: '8px',
            fontWeight: 'bold'
          }}>
            VIP
          </span>
        )}
        
        {/* 官方认证徽章 */}
        {conversation.isOfficial && (
          <span style={{
            backgroundColor: '#4CAF50',
            color: 'white',
            fontSize: '10px',
            padding: '2px 6px',
            borderRadius: '8px',
            fontWeight: 'bold'
          }}>
            官方
          </span>
        )}
        
        {/* 群组成员数量 */}
        {conversation.type === 'GROUP' && (
          <span style={{
            color: '#999',
            fontSize: '12px'
          }}>
            ({conversation.memberCount})
          </span>
        )}
      </div>
    );
  };

  return (
    <ConversationPreview
      conversation={conversation}
      Title={createTitleWithBadge(conversation)}
    />
  );
}
```

#### Example2: 搜索高亮标题

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';

function ConversationWithHighlightTitle() {
  const [searchKeyword, setSearchKeyword] = useState('');

  const createHighlightTitle = (conversation: ConversationModel, keyword: string) => {
    const name = conversation.getShowName();
    
    if (!keyword) {
      return <span>{name}</span>;
    }

    const parts = name.split(new RegExp(`(${keyword})`, 'gi'));
    
    return (
      <span>
        {parts.map((part, index) => (
          part.toLowerCase() === keyword.toLowerCase() ? (
            <span 
              key={index}
              style={{
                backgroundColor: '#FFEB3B',
                color: '#333',
                fontWeight: 'bold',
                padding: '1px 2px',
                borderRadius: '2px'
              }}
            >
              {part}
            </span>
          ) : (
            <span key={index}>{part}</span>
          )
        ))}
      </span>
    );
  };

  return (
    <div>
      <input
        type="text"
        placeholder="搜索会话..."
        value={searchKeyword}
        onChange={(e) => setSearchKeyword(e.target.value)}
        style={{ marginBottom: '16px', padding: '8px', width: '100%' }}
      />
      
      {filteredConversations.map(conversation => (
        <ConversationPreview
          key={conversation.conversationID}
          conversation={conversation}
          Title={createHighlightTitle(conversation, searchKeyword)}
          highlightMatchString={searchKeyword}
        />
      ))}
    </div>
  );
}
```

### onSelectConversation

type: `(conversation: ConversationModel) => void`

onSelectConversation 用于处理用户点击会话时的回调函数，默认值为 `-`。

#### Example1: 基础会话选择处理

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';
import { useState } from 'react';

function ConversationListWithSelection() {
  const [activeConversation, setActiveConversation] = useState<ConversationModel | null>(null);
  const [conversationHistory, setConversationHistory] = useState<ConversationModel[]>([]);

  const handleSelectConversation = (conversation: ConversationModel) => {
    // 设置当前活跃会话
    setActiveConversation(conversation);
    
    // 更新访问历史
    setConversationHistory(prev => {
      const filtered = prev.filter(c => c.conversationID !== conversation.conversationID);
      return [conversation, ...filtered].slice(0, 10); // 保留最近10个
    });
    
    // 标记会话为已读
    conversation.markAsRead();
    
    // 记录用户行为
    analytics.track('conversation_selected', {
      conversationId: conversation.conversationID,
      conversationType: conversation.type,
      hasUnreadMessages: conversation.unreadCount > 0
    });
    
    // 路由跳转（如果是SPA应用）
    router.push(`/chat/${conversation.conversationID}`);
    
    console.log('选中会话:', conversation.getShowName());
  };

  return (
    <div>
      {conversationList.map(conversation => (
        <ConversationPreview
          key={conversation.conversationID}
          conversation={conversation}
          isSelected={activeConversation?.conversationID === conversation.conversationID}
          onSelectConversation={handleSelectConversation}
        />
      ))}
      
      {/* 显示访问历史 */}
      {conversationHistory.length > 0 && (
        <div style={{ marginTop: '20px', padding: '10px', backgroundColor: '#f5f5f5' }}>
          <h4>最近访问</h4>
          {conversationHistory.slice(0, 5).map(conv => (
            <div key={conv.conversationID} style={{ fontSize: '12px', color: '#666' }}>
              {conv.getShowName()}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}
```

#### Example2: 权限控制和状态管理

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';
import { useState, useCallback } from 'react';

function ConversationListWithPermissionControl() {
  const [selectedConversation, setSelectedConversation] = useState<ConversationModel | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const handleSelectConversation = useCallback(async (conversation: ConversationModel) => {
    try {
      setLoading(true);
      setError(null);
      
      // 权限检查
      const hasPermission = await checkConversationPermission(conversation);
      if (!hasPermission) {
        setError('您没有权限访问此会话');
        showErrorToast('没有访问权限');
        return;
      }
      
      // 检查会话状态
      if (conversation.isDisabled) {
        setError('此会话已被禁用');
        showWarningToast('会话已被禁用');
        return;
      }
      
      // 预加载会话数据
      await preloadConversationData(conversation);
      
      // 更新选中状态
      setSelectedConversation(conversation);
      
      // 更新用户状态
      await updateUserActiveStatus(conversation.conversationID);
      
      // 发送会话进入事件
      await sendConversationEnterEvent(conversation);
      
      // 统计和埋点
      reportConversationAccess({
        conversationId: conversation.conversationID,
        conversationType: conversation.type,
        memberCount: conversation.memberCount,
        hasUnreadMessages: conversation.unreadCount > 0,
        accessTime: new Date().toISOString()
      });
      
      // 成功提示
      showSuccessToast(`已进入 ${conversation.getShowName()}`);
      
    } catch (err) {
      console.error('选择会话失败:', err);
      setError('选择会话失败，请重试');
      showErrorToast('操作失败，请重试');
    } finally {
      setLoading(false);
    }
  }, []);

  return (
    <div>
      {/* 错误提示 */}
      {error && (
        <div style={{
          backgroundColor: '#ffebee',
          color: '#c62828',
          padding: '8px 12px',
          borderRadius: '4px',
          marginBottom: '12px',
          fontSize: '14px'
        }}>
          {error}
          <button 
            onClick={() => setError(null)}
            style={{ marginLeft: '8px', color: '#c62828' }}
          >
            ✕
          </button>
        </div>
      )}
      
      {/* 加载状态 */}
      {loading && (
        <div style={{
          backgroundColor: '#e3f2fd',
          color: '#1976d2',
          padding: '8px 12px',
          borderRadius: '4px',
          marginBottom: '12px',
          fontSize: '14px'
        }}>
          正在加载会话...
        </div>
      )}
      
      {/* 会话列表 */}
      {conversationList.map(conversation => (
        <ConversationPreview
          key={conversation.conversationID}
          conversation={conversation}
          isSelected={selectedConversation?.conversationID === conversation.conversationID}
          onSelectConversation={handleSelectConversation}
          style={{
            opacity: loading ? 0.6 : 1,
            pointerEvents: loading ? 'none' : 'auto'
          }}
        />
      ))}
    </div>
  );
} 
```

### actionsConfig

type: `ConversationActionsConfig`

actionsConfig 用于配置会话操作功能，包括置顶、静音、删除等操作的启用状态和自定义行为，默认值为 `-`。

#### Example1: 基础操作配置

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';
import type { ConversationActionsConfig } from 'tuikit-atomicx-react';

function ConversationWithBasicActions() {
  const actionsConfig: ConversationActionsConfig = {
    // 启用基础操作
    enablePin: true,
    enableMute: true,
    enableDelete: true,
    enableMarkUnread: true,
    
    // 自定义操作处理
    onConversationPin: (conversation, e) => {
      e?.stopPropagation();
      
      if (conversation.isPinned) {
        // 取消置顶
        conversation.unpin();
        showSuccessToast(`已取消置顶 ${conversation.getShowName()}`);
      } else {
        // 置顶会话
        conversation.pin();
        showSuccessToast(`已置顶 ${conversation.getShowName()}`);
      }
      
      // 刷新会话列表
      refreshConversationList();
    },
    
    onConversationMute: (conversation, e) => {
      e?.stopPropagation();
      
      if (conversation.isMuted) {
        // 取消静音
        conversation.unmute();
        showSuccessToast(`已取消静音 ${conversation.getShowName()}`);
      } else {
        // 静音会话
        conversation.mute();
        showSuccessToast(`已静音 ${conversation.getShowName()}`);
      }
    },
    
    onConversationDelete: async (conversation, e) => {
      e?.stopPropagation();
      
      // 确认删除
      const confirmed = await showConfirmDialog(
        '删除会话',
        `确定要删除与 ${conversation.getShowName()} 的会话吗？删除后聊天记录将无法恢复。`
      );
      
      if (confirmed) {
        try {
          await conversation.delete();
          showSuccessToast('会话已删除');
          refreshConversationList();
        } catch (error) {
          showErrorToast('删除失败，请重试');
        }
      }
    },
    
    onMarkConversationUnread: (conversation, e) => {
      e?.stopPropagation();
      
      conversation.markAsUnread();
      showSuccessToast(`已标记为未读`);
    }
  };

  return (
    <ConversationPreview
      conversation={conversation}
      enableActions={true}
      actionsConfig={actionsConfig}
    />
  );
}
```

#### Example2: 高级自定义操作

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';
import type { ConversationActionsConfig } from 'tuikit-atomicx-react';

function ConversationWithAdvancedActions() {
  const actionsConfig: ConversationActionsConfig = {
    // 基础操作
    enablePin: true,
    enableMute: true,
    enableDelete: false, // 禁用默认删除
    enableMarkUnread: true,
    
    // 自定义操作
    customConversationActions: {
      // 添加到群组
      addToGroup: {
        enable: true,
        label: '添加到群组',
        onClick: (conversation, e) => {
          e?.stopPropagation();
          openAddToGroupModal(conversation);
        }
      },
      
      // 设置备注
      setRemark: {
        enable: conversation.type === 'C2C', // 仅私聊可用
        label: '设置备注',
        onClick: (conversation, e) => {
          e?.stopPropagation();
          openRemarkModal(conversation);
        }
      },
      
      // 查看资料
      viewProfile: {
        enable: true,
        label: conversation.type === 'GROUP' ? '群资料' : '个人资料',
        onClick: (conversation, e) => {
          e?.stopPropagation();
          if (conversation.type === 'GROUP') {
            openGroupProfileModal(conversation);
          } else {
            openUserProfileModal(conversation);
          }
        }
      },
      
      // 导出聊天记录
      exportChat: {
        enable: hasExportPermission(conversation),
        label: '导出聊天记录',
        onClick: async (conversation, e) => {
          e?.stopPropagation();
          
          try {
            showLoadingToast('正在导出...');
            const exportData = await exportChatHistory(conversation);
            downloadFile(exportData, `${conversation.getShowName()}_聊天记录.txt`);
            showSuccessToast('导出成功');
          } catch (error) {
            showErrorToast('导出失败');
          }
        }
      },
      
      // 举报
      report: {
        enable: conversation.type === 'C2C',
        label: '举报',
        onClick: (conversation, e) => {
          e?.stopPropagation();
          openReportModal(conversation);
        }
      }
    },
    
    // 自定义弹出菜单图标
    PopupIcon: (
      <div style={{ 
        width: '20px', 
        height: '20px', 
        display: 'flex', 
        alignItems: 'center', 
        justifyContent: 'center',
        borderRadius: '4px',
        backgroundColor: '#f0f0f0',
        color: '#666'
      }}>
        ⋯
      </div>
    ),
    
    // 点击事件处理
    onClick: (e, key, conversation) => {
      console.log('操作点击:', { key, conversation: conversation?.getShowName() });
      
      // 埋点统计
      analytics.track('conversation_action_click', {
        action: key,
        conversationId: conversation?.conversationID,
        conversationType: conversation?.type
      });
    },
    
    // 关闭事件处理
    onClose: () => {
      console.log('操作菜单关闭');
    }
  };

  return (
    <ConversationPreview
      conversation={conversation}
      enableActions={true}
      actionsConfig={actionsConfig}
    />
  );
}
```

### children

type: `React.ReactNode`

children 用于完全自定义会话预览的内容，当提供此属性时，将完全替换默认的会话预览UI，默认值为 `-`。

#### Example1: 自定义会话预览布局

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';
import { useState } from 'react';

function ConversationWithCustomChildren() {
  const [isHovered, setIsHovered] = useState(false);

  const customChildren = (
    <div 
      style={{
        display: 'flex',
        flexDirection: 'column',
        padding: '12px 16px',
        backgroundColor: isHovered ? '#f8f9fa' : 'transparent',
        borderRadius: '8px',
        cursor: 'pointer',
        transition: 'all 0.2s ease'
      }}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      {/* 顶部区域：头像和基本信息 */}
      <div style={{ display: 'flex', alignItems: 'center', marginBottom: '8px' }}>
        <img
          src={conversation.getAvatar()}
          alt={conversation.getShowName()}
          style={{
            width: '48px',
            height: '48px',
            borderRadius: '50%',
            marginRight: '12px',
            objectFit: 'cover'
          }}
        />
        
        <div style={{ flex: 1 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <h3 style={{ 
              margin: 0, 
              fontSize: '16px', 
              fontWeight: '600',
              color: '#1a1a1a'
            }}>
              {conversation.getShowName()}
            </h3>
            
            <span style={{ fontSize: '12px', color: '#8e8e93' }}>
              {formatTime(conversation.lastMessage?.lastTime)}
            </span>
          </div>
          
          {/* 会话类型标识 */}
          <div style={{ display: 'flex', alignItems: 'center', gap: '6px', marginTop: '2px' }}>
            {conversation.type === 'GROUP' && (
              <span style={{
                backgroundColor: '#007AFF',
                color: 'white',
                fontSize: '10px',
                padding: '2px 6px',
                borderRadius: '10px'
              }}>
                群聊
              </span>
            )}
            
            {conversation.isPinned && (
              <span style={{ fontSize: '12px', color: '#FF9500' }}>📌</span>
            )}
            
            {conversation.isMuted && (
              <span style={{ fontSize: '12px', color: '#8e8e93' }}>🔇</span>
            )}
          </div>
        </div>
      </div>
      
      {/* 底部区域：消息预览和未读数 */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <p style={{
            margin: 0,
            fontSize: '14px',
            color: '#8e8e93',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap'
          }}>
            {getLastMessagePreview(conversation)}
          </p>
        </div>
        
        {conversation.unreadCount > 0 && (
          <div style={{
            backgroundColor: '#FF3B30',
            color: 'white',
            borderRadius: '12px',
            padding: '4px 8px',
            fontSize: '12px',
            fontWeight: 'bold',
            minWidth: '20px',
            textAlign: 'center',
            marginLeft: '8px'
          }}>
            {conversation.unreadCount > 99 ? '99+' : conversation.unreadCount}
          </div>
        )}
      </div>
      
      {/* 操作按钮（悬停时显示） */}
      {isHovered && (
        <div style={{
          display: 'flex',
          justifyContent: 'flex-end',
          gap: '8px',
          marginTop: '8px',
          paddingTop: '8px',
          borderTop: '1px solid #e5e5ea'
        }}>
          <button
            style={{
              padding: '4px 8px',
              fontSize: '12px',
              backgroundColor: '#f2f2f7',
              border: 'none',
              borderRadius: '4px',
              cursor: 'pointer'
            }}
            onClick={(e) => {
              e.stopPropagation();
              conversation.pin();
            }}
          >
            {conversation.isPinned ? '取消置顶' : '置顶'}
          </button>
          
          <button
            style={{
              padding: '4px 8px',
              fontSize: '12px',
              backgroundColor: '#f2f2f7',
              border: 'none',
              borderRadius: '4px',
              cursor: 'pointer'
            }}
            onClick={(e) => {
              e.stopPropagation();
              conversation.mute();
            }}
          >
            {conversation.isMuted ? '取消静音' : '静音'}
          </button>
        </div>
      )}
    </div>
  );

  return (
    <ConversationPreview
      conversation={conversation}
      onSelectConversation={(conv) => {
        console.log('选中会话:', conv.getShowName());
      }}
    >
      {customChildren}
    </ConversationPreview>
  );
}
```

#### Example2: 极简风格自定义布局

```tsx
import { ConversationPreview } from 'tuikit-atomicx-react';

function MinimalConversationPreview() {
  const customChildren = (
    <div style={{
      display: 'flex',
      alignItems: 'center',
      padding: '8px 12px',
      borderBottom: '1px solid #f0f0f0',
      cursor: 'pointer'
    }}>
      {/* 简化头像 */}
      <div
        style={{
          width: '36px',
          height: '36px',
          borderRadius: '50%',
          backgroundColor: '#e0e0e0',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          marginRight: '12px',
          fontSize: '16px',
          fontWeight: 'bold',
          color: '#666'
        }}
      >
        {conversation.getShowName().charAt(0)}
      </div>
      
      {/* 会话信息 */}
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{
          fontSize: '14px',
          fontWeight: '500',
          color: '#333',
          overflow: 'hidden',
          textOverflow: 'ellipsis',
          whiteSpace: 'nowrap'
        }}>
          {conversation.getShowName()}
        </div>
      </div>
      
      {/* 状态指示器 */}
      <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
        {conversation.unreadCount > 0 && (
          <div style={{
            width: '8px',
            height: '8px',
            borderRadius: '50%',
            backgroundColor: '#FF4444'
          }} />
        )}
        
        {conversation.isPinned && (
          <span style={{ fontSize: '12px', color: '#FF9500' }}>📌</span>
        )}
      </div>
    </div>
  );

  return (
    <ConversationPreview
      conversation={conversation}
      onSelectConversation={(conv) => {
        console.log('选中会话:', conv.getShowName());
      }}
    >
      {customChildren}
    </ConversationPreview>
  );
}
```

### className

type: `string`

className 用于自定义组件的样式类名，默认值为 `-`。

### style

type: `React.CSSProperties`

style 用于自定义组件的内联样式，默认值为 `-`。