# 🦟 蚊子项目 - React组件库

这是蚊子项目的React组件库，提供完整的分享功能集成。

## 📦 安装

```bash
npm install @mosquito/react
# 或
yarn add @mosquito/react
# 或
pnpm add @mosquito/react
```

## 🚀 快速开始

### 基础配置

```typescript
// src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { MosquitoProvider } from '@mosquito/react'

const root = ReactDOM.createRoot(document.getElementById('root')!)
root.render(
  <React.StrictMode>
    <MosquitoProvider
      baseUrl="https://api.your-domain.com"
      apiKey="your-api-key"
      timeout={10000}
    >
      <App />
    </MosquitoProvider>
  </React.StrictMode>
)
```

## 📖 组件文档

### MosquitoShareButton

分享按钮组件，支持一键复制链接到剪贴板。

#### Props

| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| activityId | `number` | - | 活动ID（必需） |
| userId | `number` | - | 用户ID（必需） |
| template | `string` | 'default' | 分享模板 |
| text | `string` | '分享给好友' | 按钮文字 |
| variant | `'primary'\|'secondary'\|'success'\|'danger'` | 'primary' | 按钮样式 |
| size | `'sm'\|'md'\|'lg'` | 'md' | 按钮大小 |
| disabled | `boolean` | false | 是否禁用 |

#### Events

| 事件 | 参数 | 说明 |
|------|------|------|
| onCopy | - | 链接已复制到剪贴板 |
| onError | `Error` | 获取分享链接失败 |

#### 示例

```tsx
import { MosquitoShareButton } from '@mosquito/react'
import { useState } from 'react'

function SharePage() {
  const activityId = 1
  const userId = 100
  const [message, setMessage] = useState('')

  return (
    <div>
      <MosquitoShareButton
        activityId={activityId}
        userId={userId}
        template="default"
        text="立即分享"
        variant="primary"
        size="lg"
        onCopy={() => setMessage('分享链接已复制到剪贴板')}
        onError={(error) => setMessage(`错误: ${error.message}`)}
      />
      {message && <p>{message}</p>}
    </div>
  )
}
```

### MosquitoPosterCard

海报展示组件，支持加载状态和错误处理。

#### Props

| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| activityId | `number` | - | 活动ID（必需） |
| userId | `number` | - | 用户ID（必需） |
| template | `string` | 'default' | 海报模板 |
| width | `string` | '300px' | 宽度 |
| height | `string` | '400px' | 高度 |
| lazy | `boolean` | false | 是否懒加载 |

#### Events

| 事件 | 参数 | 说明 |
|------|------|------|
| onLoad | - | 海报加载完成 |
| onError | `Error` | 海报加载失败 |
| onClick | - | 海报被点击 |

#### 示例

```tsx
import { MosquitoPosterCard } from '@mosquito/react'
import { useState } from 'react'

function PosterPage() {
  const activityId = 1
  const userId = 100
  const [loading, setLoading] = useState(true)

  return (
    <div>
      <MosquitoPosterCard
        activityId={activityId}
        userId={userId}
        template="premium"
        width="350px"
        height="500px"
        lazy
        onLoad={() => setLoading(false)}
        onError={(error) => console.error('海报加载失败:', error)}
        onClick={() => console.log('海报被点击')}
      />
      {loading && <p>加载中...</p>}
    </div>
  )
}
```

### MosquitoLeaderboard

排行榜组件，支持分页和排序。

#### Props

| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| activityId | `number` | - | 活动ID（必需） |
| page | `number` | 0 | 页码 |
| pageSize | `number` | 20 | 每页大小 |
| topN | `number` | undefined | 只显示前N名 |
| currentUserId | `number` | undefined | 当前用户ID |
| sortable | `boolean` | false | 是否支持排序 |
| exportable | `boolean` | false | 是否显示导出按钮 |

#### Events

| 事件 | 参数 | 说明 |
|------|------|------|
| onLoad | `entries[]` | 排行榜数据加载完成 |
| onError | `Error` | 排行榜加载失败 |
| onPageChange | `number` | 页码变化 |
| onExport | - | 导出排行榜 |

#### 示例

```tsx
import { MosquitoLeaderboard } from '@mosquito/react'
import { useState } from 'react'

function LeaderboardPage() {
  const activityId = 1
  const [page, setPage] = useState(0)
  const currentUserId = 100

  return (
    <div>
      <MosquitoLeaderboard
        activityId={activityId}
        page={page}
        pageSize={20}
        topN={10}
        currentUserId={currentUserId}
        sortable
        exportable
        onLoad={(entries) => console.log('加载完成:', entries)}
        onError={(error) => console.error('加载失败:', error)}
        onPageChange={(newPage) => setPage(newPage)}
        onExport={() => console.log('导出排行榜')}
      />
    </div>
  )
}
```

### MosquitoShareModal

分享弹窗组件，提供多种分享方式。

#### Props

| 属性 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| activityId | `number` | - | 活动ID（必需） |
| userId | `number` | - | 用户ID（必需） |
| open | `boolean` | - | 是否打开 |
| onClose | `() => void` | - | 关闭回调 |
| title | `string` | '分享活动' | 弹窗标题 |

#### 示例

```tsx
import { MosquitoShareModal } from '@mosquito/react'
import { useState } from 'react'

function ShareModalPage() {
  const activityId = 1
  const userId = 100
  const [open, setOpen] = useState(false)

  return (
    <div>
      <button onClick={() => setOpen(true)}>打开分享弹窗</button>
      
      <MosquitoShareModal
        activityId={activityId}
        userId={userId}
        open={open}
        onClose={() => setOpen(false)}
      />
    </div>
  )
}
```

## 🔧 Hooks

### useMosquito

核心Hook，提供API调用功能。

```typescript
import { useMosquito } from '@mosquito/react'

function MyComponent() {
  const {
    // 活动管理
    getActivity,
    createActivity,
    
    // 分享功能
    getShareUrl,
    getShareMeta,
    
    // 海报功能
    getPosterImage,
    getPosterHtml,
    
    // 排行榜
    getLeaderboard,
    exportLeaderboard,
    
    // 状态
    loading,
    error,
    
    // 配置
    config
  } = useMosquito()

  const handleGetShareUrl = async () => {
    try {
      const url = await getShareUrl(1, 100)
      console.log('分享链接:', url)
    } catch (err) {
      console.error('获取分享链接失败:', err)
    }
  }

  return (
    <button onClick={handleGetShareUrl}>
      获取分享链接
    </button>
  )
}
```

### useShareUrl

获取分享链接的专用Hook。

```typescript
import { useShareUrl } from '@mosquito/react'

function ShareUrlComponent({ activityId, userId }: { activityId: number; userId: number }) {
  const { shareUrl, loading, error, fetchShareUrl } = useShareUrl(activityId, userId)

  return (
    <div>
      {loading && <p>加载中...</p>}
      {error && <p>错误: {error.message}</p>}
      {shareUrl && (
        <div>
          <p>分享链接: {shareUrl}</p>
          <button onClick={() => fetchShareUrl()}>刷新</button>
        </div>
      )}
    </div>
  )
}
```

### usePoster

海报功能的专用Hook。

```typescript
import { usePoster } from '@mosquito/react'

function PosterComponent({ activityId, userId }: { activityId: number; userId: number }) {
  const { posterUrl, loading, error, fetchPoster } = usePoster(activityId, userId, 'default')

  return (
    <div>
      {loading && <p>加载中...</p>}
      {error && <p>错误: {error.message}</p>}
      {posterUrl && (
        <div>
          <img src={posterUrl} alt="分享海报" />
          <button onClick={() => fetchPoster()}>刷新海报</button>
        </div>
      )}
    </div>
  )
}
```

### useLeaderboard

排行榜的专用Hook。

```typescript
import { useLeaderboard } from '@mosquito/react'

function LeaderboardComponent({ activityId }: { activityId: number }) {
  const { entries, loading, error, pagination, fetchLeaderboard, changePage } = useLeaderboard(activityId)

  return (
    <div>
      {loading && <p>加载中...</p>}
      {error && <p>错误: {error.message}</p>}
      {entries && (
        <div>
          <ul>
            {entries.map((entry, index) => (
              <li key={entry.userId}>
                #{index + 1} - {entry.userName}: {entry.score}分
              </li>
            ))}
          </ul>
          <button onClick={() => changePage(pagination.page - 1)} disabled={pagination.page === 0}>
            上一页
          </button>
          <span>第 {pagination.page + 1} 页</span>
          <button onClick={() => changePage(pagination.page + 1)} disabled={pagination.page >= pagination.totalPages - 1}>
            下一页
          </button>
        </div>
      )}
    </div>
  )
}
```

## 🎨 自定义主题

### 使用主题提供者

```tsx
import { MosquitoProvider, MosquitoTheme } from '@mosquito/react'

const customTheme: MosquitoTheme = {
  colors: {
    primary: '#ff6b00',
    secondary: '#6c757d',
    success: '#28a745',
    danger: '#dc3545',
    warning: '#ffc107',
  },
  components: {
    Button: {
      borderRadius: '8px',
      boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
    },
    Card: {
      borderRadius: '12px',
      boxShadow: '0 4px 8px rgba(0,0,0,0.15)',
    },
  },
}

function App() {
  return (
    <MosquitoProvider
      baseUrl="https://api.your-domain.com"
      apiKey="your-api-key"
      theme={customTheme}
    >
      <YourApp />
    </MosquitoProvider>
  )
}
```

## 📝 TypeScript类型

### 类型定义

```typescript
import type {
  Activity,
  LeaderboardEntry,
  ShareMeta,
  PosterConfig,
  ApiResponse,
  MosquitoConfig,
  MosquitoTheme
} from '@mosquito/react'

// Activity类型
interface Activity {
  id: number
  name: string
  startTime: Date
  endTime: Date
  status: 'draft' | 'active' | 'completed'
}

// LeaderboardEntry类型
interface LeaderboardEntry {
  userId: number
  userName: string
  score: number
  rank?: number
  inviteCount?: number
}

// ShareMeta类型
interface ShareMeta {
  title: string
  description: string
  image: string
  url: string
}

// PosterConfig类型
interface PosterConfig {
  template: string
  imageUrl: string
  htmlUrl: string
}

// ApiResponse类型
interface ApiResponse<T> {
  code: number
  message: string
  data: T
  meta?: {
    page: number
    size: number
    total: number
    totalPages: number
  }
}

// MosquitoConfig类型
interface MosquitoConfig {
  baseUrl: string
  apiKey: string
  timeout?: number
  retryCount?: number
  enableLogging?: boolean
  theme?: MosquitoTheme
}

// MosquitoTheme类型
interface MosquitoTheme {
  colors: {
    primary: string
    secondary: string
    success: string
    danger: string
    warning: string
  }
  components: {
    Button?: React.CSSProperties
    Card?: React.CSSProperties
  }
}
```

## 🧪 测试

### 单元测试

```typescript
import { render, screen, fireEvent } from '@testing-library/react'
import { MosquitoProvider } from '@mosquito/react'
import { MosquitoShareButton } from '@mosquito/react'

function renderWithProviders(ui: React.ReactElement) {
  return render(
    <MosquitoProvider
      baseUrl="https://test-api.com"
      apiKey="test-key"
    >
      {ui}
    </MosquitoProvider>
  )
}

test('渲染分享按钮', () => {
  renderWithProviders(
    <MosquitoShareButton activityId={1} userId={100} />
  )
  
  const button = screen.getByText('分享给好友')
  expect(button).toBeInTheDocument()
})

test('点击按钮触发分享', async () => {
  renderWithProviders(
    <MosquitoShareButton activityId={1} userId={100} onCopy={mockOnCopy} />
  )
  
  const button = screen.getByText('分享给好友')
  fireEvent.click(button)
  
  await waitFor(() => {
    expect(mockOnCopy).toHaveBeenCalled()
  })
})
```

## 📚 最佳实践

### 1. 错误处理

```tsx
function GoodErrorHandling() {
  const { getShareUrl, error } = useMosquito()
  const [localError, setLocalError] = useState<Error | null>(null)

  const handleShare = async () => {
    try {
      await getShareUrl(1, 100)
    } catch (err) {
      setLocalError(err as Error)
    }
  }

  return (
    <div>
      <button onClick={handleShare}>分享</button>
      {(error || localError) && (
        <div className="error-message">
          {(error || localError)?.message}
        </div>
      )}
    </div>
  )
}
```

### 2. 加载状态

```tsx
function GoodLoadingState() {
  const { getShareUrl, loading } = useMosquito()

  return (
    <button onClick={() => getShareUrl(1, 100)} disabled={loading}>
      {loading ? '分享中...' : '分享给好友'}
    </button>
  )
}
```

### 3. 类型安全

```tsx
function TypeSafeComponent() {
  const [activity, setActivity] = useState<Activity | null>(null)
  const { getActivity } = useMosquito()

  useEffect(() => {
    const loadActivity = async () => {
      const data = await getActivity(1)
      setActivity(data)
    }
    
    loadActivity()
  }, [getActivity])

  return (
    <div>
      {activity && (
        <div>
          <h2>{activity.name}</h2>
          <p>开始时间: {activity.startTime.toLocaleDateString()}</p>
          <p>结束时间: {activity.endTime.toLocaleDateString()}</p>
        </div>
      )}
    </div>
  )
}
```

## 🤝 贡献

欢迎提交Issue和Pull Request！

---

*React组件库版本: v2.0.0*  
*最后更新: 2026-01-22*