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

633 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🦟 蚊子项目 - 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*