npm package: @machengji/chat-ui-react
Description: A reusable React chat UI component library for Chatwoot integration
Author: Your Name
Latest version: 1.1.6

# Chatwoot React UI

一个可复用的 React 聊天 UI 组件库，支持 Chatwoot 集成。该组件库提供了完整的聊天界面，包括会话列表、消息显示、用户管理等功能。

## 特性

- 🎨 现代化的聊天 UI 设计
- 📱 响应式布局，支持移动端
- 🔧 高度可定制，支持外部数据驱动
- 💬 支持私聊和群聊
- 🔍 用户搜索功能
- 📎 文件附件支持
- ⌨️ 打字状态显示
- ✅ 消息已读状态
- 🎯 TypeScript 支持

## 安装

```bash
npm install chatwoot-react-ui
# 或
yarn add chatwoot-react-ui
# 或
pnpm add chatwoot-react-ui
```

**前置要求**：需要 React 19 或更高版本

## 基础使用

### SimpleChatUI - 简化版本

```tsx
import React, { useState } from 'react';
import { SimpleChatUI, User, Dialog, Message } from 'chatwoot-react-ui';

const App = () => {
  const [selectedDialog, setSelectedDialog] = useState<Dialog | null>(null);
  const [messages, setMessages] = useState<Record<string, Message[]>>({});

  const handleSelectDialog = (dialog: Dialog) => {
    setSelectedDialog(dialog);
  };

  const handleSendMessage = (text: string) => {
    if (!selectedDialog) return;
    
    const newMessage: Message = {
      _id: Date.now().toString(),
      chat_dialog_id: selectedDialog._id,
      sender_id: 1,
      message: text,
      date_sent: Date.now(),
      read: 0,
    };

    setMessages(prev => ({
      ...prev,
      [selectedDialog._id]: [...(prev[selectedDialog._id] || []), newMessage],
    }));
  };

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <SimpleChatUI
        dialogs={dialogs}
        messages={messages}
        users={users}
        selectedDialog={selectedDialog}
        currentUserId={1}
        onSelectDialog={handleSelectDialog}
        onSendMessage={handleSendMessage}
      />
    </div>
  );
};
```

### ChatUI - 完整功能版本

```tsx
import React, { useState, useCallback } from 'react';
import { ChatUI, User, Dialog, Message } from 'chatwoot-react-ui';

const App = () => {
  const [dialogs, setDialogs] = useState<Dialog[]>([]);
  const [messages, setMessages] = useState<Record<string, Message[]>>({});
  const [users, setUsers] = useState<Record<number, User>>({});
  const [selectedDialog, setSelectedDialog] = useState<Dialog | null>(null);

  const handleSelectDialog = useCallback((dialog: Dialog) => {
    setSelectedDialog(dialog);
    // 加载消息逻辑
  }, []);

  const handleSendMessage = useCallback((text: string) => {
    // 发送消息逻辑
  }, [selectedDialog]);

  const handleSendMessageWithAttachment = useCallback((file: File) => {
    // 发送附件逻辑
  }, []);

  const handleSendTypingStatus = useCallback((isTyping: boolean) => {
    // 打字状态逻辑
  }, []);

  const handleReadMessage = useCallback((messageId: string, senderId: number, dialogId: string) => {
    // 已读状态逻辑
  }, []);

  const handleSearchUsers = useCallback(async (term: string): Promise<User[]> => {
    // 用户搜索逻辑
    return [];
  }, []);

  const handleCreateChat = useCallback(async (opponentId: number): Promise<Dialog> => {
    // 创建私聊逻辑
    return {} as Dialog;
  }, []);

  const handleCreateGroupChat = useCallback(async (userIds: number[], name: string): Promise<Dialog> => {
    // 创建群聊逻辑
    return {} as Dialog;
  }, []);

  const handleAddUsersToGroupChat = useCallback(async (userIds: number[]) => {
    // 添加用户到群聊逻辑
  }, []);

  return (
    <div style={{ width: '100vw', height: '100vh' }}>
      <ChatUI
        dialogs={dialogs}
        messages={messages}
        users={users}
        selectedDialog={selectedDialog}
        currentUserId={1}
        onSelectDialog={handleSelectDialog}
        onSendMessage={handleSendMessage}
        onSendMessageWithAttachment={handleSendMessageWithAttachment}
        onSendTypingStatus={handleSendTypingStatus}
        onReadMessage={handleReadMessage}
        onSearchUsers={handleSearchUsers}
        onCreateChat={handleCreateChat}
        onCreateGroupChat={handleCreateGroupChat}
        onAddUsersToGroupChat={handleAddUsersToGroupChat}
        showUsersTab={true}
        className="border border-gray-300 rounded-lg"
        style={{ maxWidth: '1200px', margin: '0 auto' }}
      />
    </div>
  );
};
```

## API 参考

### SimpleChatUI Props

| 属性 | 类型 | 必需 | 描述 |
|------|------|------|------|
| dialogs | Dialog[] | ✅ | 会话列表 |
| messages | Record<string, Message[]> | ✅ | 消息数据 |
| users | Record<number, User> | ✅ | 用户数据 |
| selectedDialog | Dialog \| null | ✅ | 当前选中的会话 |
| currentUserId | number | ✅ | 当前用户ID |
| onSelectDialog | (dialog: Dialog) => void | ✅ | 选择会话回调 |
| onSendMessage | (text: string) => void | ✅ | 发送消息回调 |
| showUsersTab | boolean | ❌ | 是否显示用户标签页 |
| className | string | ❌ | 自定义CSS类名 |
| style | React.CSSProperties | ❌ | 自定义样式 |

### ChatUI Props

包含 SimpleChatUI 的所有属性，另外还有：

| 属性 | 类型 | 必需 | 描述 |
|------|------|------|------|
| onSendMessageWithAttachment | (file: File) => void | ❌ | 发送附件回调 |
| onSendTypingStatus | (isTyping: boolean) => void | ❌ | 打字状态回调 |
| onReadMessage | (messageId: string, senderId: number, dialogId: string) => void | ❌ | 已读消息回调 |
| onSearchUsers | (term: string) => Promise<User[]> | ❌ | 搜索用户回调 |
| onCreateChat | (opponentId: number) => Promise<Dialog> | ❌ | 创建私聊回调 |
| onCreateGroupChat | (userIds: number[], name: string) => Promise<Dialog> | ❌ | 创建群聊回调 |
| onAddUsersToGroupChat | (userIds: number[]) => Promise<void> | ❌ | 添加用户到群聊回调 |
| lastMessageSentTimeString | (dialog: Dialog) => string | ❌ | 最后消息时间格式化函数 |
| messageSentTimeString | (msg: Message) => string | ❌ | 消息时间格式化函数 |

### 数据类型

#### User
```typescript
interface User {
  id: number;
  full_name?: string;
  login?: string;
  avatar?: string;
}
```

#### Dialog
```typescript
interface Dialog {
  _id: string;
  name: string;
  photo?: string;
  type: number; // 2: group, 3: private
  last_message?: string;
  last_message_user_id?: number;
  unread_messages_count: number;
  last_message_date_sent?: number;
  created_at?: number;
  occupants_ids: number[];
}
```

#### Message
```typescript
interface Message {
  _id: string;
  chat_dialog_id: string;
  sender_id: number;
  message: string;
  date_sent: number;
  read: number;
  attachments?: Array<{ url: string }>;
}
```

## 样式定制

组件使用 Tailwind CSS 构建，你可以通过以下方式定制样式：

1. **通过 className 属性**：添加自定义CSS类
2. **通过 style 属性**：添加内联样式
3. **覆盖 Tailwind 类**：在你的项目中重新定义 Tailwind 类

## 开发

```bash
# 安装依赖
npm install

# 启动开发服务器
npm run dev

# 构建组件库
npm run build:lib

# 类型检查
npm run typecheck

# 代码检查
npm run lint
```

## 许可证

MIT License

## 贡献

欢迎提交 Issue 和 Pull Request！

## 更新日志

### v1.1.0
- 升级到 React 19 支持
- 更新 TypeScript 类型定义

### v1.0.0
- 初始版本发布
- 支持基础聊天功能
- 支持私聊和群聊
- 支持用户搜索
- 支持文件附件
- 支持打字状态
- 支持消息已读状态