Files
wenzi/frontend/README_REACT.md
Your Name 91a0b77f7a test(cache): 修复CacheConfigTest边界值测试
- 修改 shouldVerifyCacheManager_withMaximumIntegerTtl 为 shouldVerifyCacheManager_withMaximumAllowedTtl
- 使用正确的最大TTL值(10080分钟,7天)而不是 Integer.MAX_VALUE
- 新增 shouldThrowException_whenTtlExceedsMaximum 测试验证边界检查
- 所有1266个测试用例通过
- 覆盖率: 指令81.89%, 行88.48%, 分支51.55%

docs: 添加项目状态报告
- 生成 PROJECT_STATUS_REPORT.md 详细记录项目当前状态
- 包含质量指标、已完成功能、待办事项和技术债务
2026-03-02 13:31:54 +08:00

13 KiB
Raw Blame History

🦟 蚊子项目 - React组件库

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

📦 安装

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

🚀 快速开始

基础配置

// 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 获取分享链接失败

示例

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 - 海报被点击

示例

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 - 导出排行榜

示例

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 '分享活动' 弹窗标题

示例

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调用功能。

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。

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。

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。

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>
  )
}

🎨 自定义主题

使用主题提供者

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类型

类型定义

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
  }
}

🧪 测试

单元测试

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. 错误处理

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. 加载状态

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

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

3. 类型安全

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