# ConversationListContent 组件自定义文档

## 概述

ConversationListContent 是一个会话列表内容容器组件，负责管理会话列表的显示状态和占位符展示。组件根据不同的状态（加载中、空列表、错误）自动切换显示相应的占位符内容，为会话列表提供统一的状态管理和用户体验。它支持自定义占位符组件，可以灵活适配不同的业务场景和设计风格。

## Props 配置表格

| 字段 | 类型 | 默认值 | 描述 |
|------|------|--------|------|
| children | `React.ReactNode` | - | 会话列表的主要内容 |
| empty | `boolean` | `false` | 是否为空列表状态 |
| loading | `boolean` | `false` | 是否为加载中状态 |
| error | `boolean` | `false` | 是否为错误状态 |
| PlaceholderEmptyList | `React.ReactNode` | `<PlaceHolder type={PlaceHolderTypes.NO_CONVERSATIONS} />` | 空列表时的占位符组件 |
| PlaceholderLoading | `React.ReactNode` | `<PlaceHolder type={PlaceHolderTypes.LOADING} />` | 加载中时的占位符组件 |
| PlaceholderLoadError | `React.ReactNode` | `<PlaceHolder type={PlaceHolderTypes.WRONG} />` | 加载错误时的占位符组件 |
| className | `string` | - | 自定义样式类名 |
| style | `React.CSSProperties` | - | 自定义样式 |

## Props 详解

### children

type: `React.ReactNode`

children 用于渲染会话列表的主要内容，通常是会话项的集合，默认值为 `-`。

### empty

type: `boolean`

empty 用于指示当前会话列表是否为空，当为 true 时显示空列表占位符，默认值为 `false`。

### loading

type: `boolean`

loading 用于指示当前是否正在加载会话数据，当为 true 时显示加载占位符，默认值为 `false`。

### error

type: `boolean`

error 用于指示加载会话数据时是否发生错误，当为 true 时显示错误占位符，默认值为 `false`。

### PlaceholderEmptyList

type: `React.ReactNode`

PlaceholderEmptyList 用于自定义空列表时显示的占位符组件，默认值为 `<PlaceHolder type={PlaceHolderTypes.NO_CONVERSATIONS} />`。

#### Example1: 基础空列表占位符

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

function ConversationListWithEmptyPlaceholder() {
  const [conversations, setConversations] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // 模拟加载会话数据
    setTimeout(() => {
      setConversations([]); // 模拟空列表
      setLoading(false);
    }, 2000);
  }, []);

  const customEmptyPlaceholder = (
    <div className="custom-empty-placeholder">
      <div className="empty-icon">💬</div>
      <h3>暂无会话</h3>
      <p>开始新的对话吧！点击右上角的 + 按钮创建会话</p>
      <button 
        className="start-chat-btn"
        onClick={() => console.log('开始创建会话')}
      >
        开始聊天
      </button>
    </div>
  );

  return (
    <ConversationListContent
      empty={conversations.length === 0 && !loading}
      loading={loading}
      PlaceholderEmptyList={customEmptyPlaceholder}
    >
      {conversations.map(conversation => (
        <div key={conversation.id} className="conversation-item">
          {conversation.name}
        </div>
      ))}
    </ConversationListContent>
  );
}
```

#### Example2: 引导式空列表占位符

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

function ConversationListWithGuidedEmpty() {
  const [conversations, setConversations] = useState([]);
  const [loading, setLoading] = useState(true);
  const [userType, setUserType] = useState('new'); // new, returning

  useEffect(() => {
    // 获取用户类型和会话数据
    Promise.all([
      fetchUserType(),
      fetchConversations()
    ]).then(([type, convs]) => {
      setUserType(type);
      setConversations(convs);
      setLoading(false);
    });
  }, []);

  const getEmptyPlaceholder = () => {
    if (userType === 'new') {
      return (
        <div className="new-user-empty">
          <div className="welcome-animation">
            <img src="/welcome-animation.gif" alt="欢迎" />
          </div>
          <h2>欢迎使用聊天应用！</h2>
          <div className="guide-steps">
            <div className="step">
              <span className="step-number">1</span>
              <span>点击右上角 + 按钮</span>
            </div>
            <div className="step">
              <span className="step-number">2</span>
              <span>选择联系人或创建群聊</span>
            </div>
            <div className="step">
              <span className="step-number">3</span>
              <span>开始愉快的聊天</span>
            </div>
          </div>
          <button 
            className="primary-btn"
            onClick={() => startTutorial()}
          >
            开始体验
          </button>
        </div>
      );
    } else {
      return (
        <div className="returning-user-empty">
          <div className="empty-state-icon">📱</div>
          <h3>会话列表为空</h3>
          <p>您的会话可能已被清理，或者还没有开始新的对话</p>
          <div className="action-buttons">
            <button 
              className="secondary-btn"
              onClick={() => restoreConversations()}
            >
              恢复会话
            </button>
            <button 
              className="primary-btn"
              onClick={() => createNewConversation()}
            >
              新建会话
            </button>
          </div>
        </div>
      );
    }
  };

  return (
    <ConversationListContent
      empty={conversations.length === 0 && !loading}
      loading={loading}
      PlaceholderEmptyList={getEmptyPlaceholder()}
    >
      {conversations.map(conversation => (
        <div key={conversation.id} className="conversation-item">
          {conversation.name}
        </div>
      ))}
    </ConversationListContent>
  );
}
```

### PlaceholderLoading

type: `React.ReactNode`

PlaceholderLoading 用于自定义加载中时显示的占位符组件，默认值为 `<PlaceHolder type={PlaceHolderTypes.LOADING} />`。

#### Example1: 基础加载占位符

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

function ConversationListWithLoadingPlaceholder() {
  const [conversations, setConversations] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    loadConversations();
  }, []);

  const loadConversations = async () => {
    setLoading(true);
    try {
      const data = await fetchConversations();
      setConversations(data);
    } finally {
      setLoading(false);
    }
  };

  const customLoadingPlaceholder = (
    <div className="custom-loading-placeholder">
      <div className="loading-spinner">
        <div className="spinner"></div>
      </div>
      <h3>正在加载会话...</h3>
      <p>请稍候，正在为您获取最新的会话信息</p>
      <div className="loading-tips">
        <p>💡 小贴士：您可以使用搜索功能快速找到特定会话</p>
      </div>
    </div>
  );

  return (
    <ConversationListContent
      loading={loading}
      empty={conversations.length === 0 && !loading}
      PlaceholderLoading={customLoadingPlaceholder}
    >
      {conversations.map(conversation => (
        <div key={conversation.id} className="conversation-item">
          {conversation.name}
        </div>
      ))}
    </ConversationListContent>
  );
}
```

#### Example2: 骨架屏加载占位符

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

function ConversationListWithSkeletonLoading() {
  const [conversations, setConversations] = useState([]);
  const [loading, setLoading] = useState(true);
  const [loadingProgress, setLoadingProgress] = useState(0);

  useEffect(() => {
    loadConversationsWithProgress();
  }, []);

  const loadConversationsWithProgress = async () => {
    setLoading(true);
    setLoadingProgress(0);
    
    try {
      // 模拟加载进度
      const progressInterval = setInterval(() => {
        setLoadingProgress(prev => {
          if (prev >= 90) {
            clearInterval(progressInterval);
            return prev;
          }
          return prev + 10;
        });
      }, 200);

      const data = await fetchConversations();
      
      clearInterval(progressInterval);
      setLoadingProgress(100);
      
      // 延迟一点时间显示完成状态
      setTimeout(() => {
        setConversations(data);
        setLoading(false);
      }, 300);
      
    } catch (error) {
      setLoading(false);
      console.error('加载会话失败:', error);
    }
  };

  const skeletonLoadingPlaceholder = (
    <div className="skeleton-loading-placeholder">
      {/* 加载进度条 */}
      <div className="loading-progress">
        <div className="progress-bar">
          <div 
            className="progress-fill" 
            style={{ width: `${loadingProgress}%` }}
          />
        </div>
        <span className="progress-text">{loadingProgress}%</span>
      </div>
      
      {/* 骨架屏项目 */}
      <div className="skeleton-items">
        {Array.from({ length: 8 }).map((_, index) => (
          <div key={index} className="skeleton-item">
            <div className="skeleton-avatar animate-pulse" />
            <div className="skeleton-content">
              <div className="skeleton-line skeleton-title animate-pulse" />
              <div className="skeleton-line skeleton-subtitle animate-pulse" />
            </div>
            <div className="skeleton-meta">
              <div className="skeleton-time animate-pulse" />
              <div className="skeleton-badge animate-pulse" />
            </div>
          </div>
        ))}
      </div>
      
      {/* 加载提示 */}
      <div className="loading-hint">
        <span className="loading-dots">加载中</span>
      </div>
    </div>
  );

  return (
    <ConversationListContent
      loading={loading}
      empty={conversations.length === 0 && !loading}
      PlaceholderLoading={skeletonLoadingPlaceholder}
    >
      {conversations.map(conversation => (
        <div key={conversation.id} className="conversation-item">
          {conversation.name}
        </div>
      ))}
    </ConversationListContent>
  );
}
```

### PlaceholderLoadError

type: `React.ReactNode`

PlaceholderLoadError 用于自定义加载错误时显示的占位符组件，默认值为 `<PlaceHolder type={PlaceHolderTypes.WRONG} />`。

#### Example1: 基础错误占位符

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

function ConversationListWithErrorPlaceholder() {
  const [conversations, setConversations] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);
  const [errorMessage, setErrorMessage] = useState('');

  const loadConversations = async () => {
    setLoading(true);
    setError(false);
    setErrorMessage('');
    
    try {
      const data = await fetchConversations();
      setConversations(data);
    } catch (err) {
      setError(true);
      setErrorMessage(err.message || '加载失败');
      console.error('加载会话失败:', err);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    loadConversations();
  }, []);

  const customErrorPlaceholder = (
    <div className="custom-error-placeholder">
      <div className="error-icon">⚠️</div>
      <h3>加载失败</h3>
      <p className="error-message">{errorMessage}</p>
      <div className="error-actions">
        <button 
          className="retry-btn"
          onClick={loadConversations}
        >
          重试
        </button>
        <button 
          className="refresh-btn"
          onClick={() => window.location.reload()}
        >
          刷新页面
        </button>
      </div>
      <div className="error-tips">
        <p>如果问题持续存在，请检查网络连接或联系技术支持</p>
      </div>
    </div>
  );

  return (
    <ConversationListContent
      loading={loading}
      error={error}
      empty={conversations.length === 0 && !loading && !error}
      PlaceholderLoadError={customErrorPlaceholder}
    >
      {conversations.map(conversation => (
        <div key={conversation.id} className="conversation-item">
          {conversation.name}
        </div>
      ))}
    </ConversationListContent>
  );
}
```

#### Example2: 智能错误诊断占位符

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

function ConversationListWithSmartErrorPlaceholder() {
  const [conversations, setConversations] = useState([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);
  const [errorDetails, setErrorDetails] = useState(null);
  const [retryCount, setRetryCount] = useState(0);

  const loadConversations = async () => {
    setLoading(true);
    setError(false);
    setErrorDetails(null);
    
    try {
      const data = await fetchConversations();
      setConversations(data);
      setRetryCount(0); // 重置重试次数
    } catch (err) {
      setError(true);
      setRetryCount(prev => prev + 1);
      
      // 错误分析
      const errorAnalysis = analyzeError(err);
      setErrorDetails(errorAnalysis);
      
      console.error('加载会话失败:', err);
      
      // 发送错误报告
      reportError(err, {
        retryCount,
        timestamp: Date.now(),
        userAgent: navigator.userAgent
      });
      
    } finally {
      setLoading(false);
    }
  };

  const analyzeError = (error) => {
    if (!navigator.onLine) {
      return {
        type: 'network',
        title: '网络连接异常',
        description: '请检查您的网络连接',
        suggestions: ['检查WiFi或移动网络', '尝试刷新页面'],
        canRetry: true
      };
    } else if (error.status === 401) {
      return {
        type: 'auth',
        title: '登录已过期',
        description: '您的登录状态已过期，请重新登录',
        suggestions: ['点击重新登录', '清除浏览器缓存'],
        canRetry: false
      };
    } else if (error.status >= 500) {
      return {
        type: 'server',
        title: '服务器异常',
        description: '服务器暂时无法响应，请稍后重试',
        suggestions: ['稍后重试', '联系技术支持'],
        canRetry: true
      };
    } else {
      return {
        type: 'unknown',
        title: '未知错误',
        description: error.message || '发生未知错误',
        suggestions: ['重试操作', '刷新页面', '联系技术支持'],
        canRetry: true
      };
    }
  };

  const handleRetry = () => {
    if (retryCount < 3) {
      loadConversations();
    } else {
      // 超过重试次数，提供其他解决方案
      showAdvancedErrorDialog();
    }
  };

  const showAdvancedErrorDialog = () => {
    // 显示高级错误处理对话框
    console.log('显示高级错误处理选项');
  };

  useEffect(() => {
    loadConversations();
  }, []);

  const smartErrorPlaceholder = (
    <div className="smart-error-placeholder">
      <div className={`error-icon ${errorDetails?.type}`}>
        {errorDetails?.type === 'network' && '🌐'}
        {errorDetails?.type === 'auth' && '🔒'}
        {errorDetails?.type === 'server' && '🔧'}
        {errorDetails?.type === 'unknown' && '❌'}
      </div>
      
      <h3>{errorDetails?.title}</h3>
      <p className="error-description">{errorDetails?.description}</p>
      
      {/* 重试信息 */}
      {retryCount > 0 && (
        <div className="retry-info">
          <p>已重试 {retryCount} 次</p>
        </div>
      )}
      
      {/* 解决建议 */}
      <div className="error-suggestions">
        <h4>解决建议：</h4>
        <ul>
          {errorDetails?.suggestions.map((suggestion, index) => (
            <li key={index}>{suggestion}</li>
          ))}
        </ul>
      </div>
      
      {/* 操作按钮 */}
      <div className="error-actions">
        {errorDetails?.canRetry && retryCount < 3 && (
          <button 
            className="retry-btn"
            onClick={handleRetry}
          >
            重试 ({3 - retryCount} 次机会)
          </button>
        )}
        
        {errorDetails?.type === 'auth' && (
          <button 
            className="login-btn"
            onClick={() => redirectToLogin()}
          >
            重新登录
          </button>
        )}
        
        <button 
          className="refresh-btn"
          onClick={() => window.location.reload()}
        >
          刷新页面
        </button>
        
        {retryCount >= 3 && (
          <button 
            className="support-btn"
            onClick={() => contactSupport()}
          >
            联系支持
          </button>
        )}
      </div>
      
      {/* 诊断信息 */}
      {process.env.NODE_ENV === 'development' && (
        <details className="error-debug">
          <summary>调试信息</summary>
          <pre>{JSON.stringify(errorDetails, null, 2)}</pre>
        </details>
      )}
    </div>
  );

  return (
    <ConversationListContent
      loading={loading}
      error={error}
      empty={conversations.length === 0 && !loading && !error}
      PlaceholderLoadError={smartErrorPlaceholder}
    >
      {conversations.map(conversation => (
        <div key={conversation.id} className="conversation-item">
          {conversation.name}
        </div>
      ))}
    </ConversationListContent>
  );
}
```

### className

type: `string`

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

### style

type: `React.CSSProperties`

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