Skip to content

Commit 9f54219

Browse files
committed
🚧 开始一些页面的开发和迁移
1 parent a0f8023 commit 9f54219

File tree

108 files changed

+8197
-19
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+8197
-19
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,11 @@ migrations.json
4141
# System Files
4242
.DS_Store
4343
Thumbs.db
44+
45+
# Playwright
46+
/e2e/test-results/
47+
/e2e/report/
48+
/playwright-report/
49+
/blob-report/
50+
/playwright/.cache/
51+
# 注意: e2e/__screenshots__/ 基线截图需要提交到 git

SERVICE-SPEC.md

Lines changed: 291 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2148,5 +2148,295 @@ src/services/
21482148

21492149
---
21502150

2151-
*文档版本: 2.0 (强类型安全版)*
2151+
## 12. Platform Services(平台服务层)
2152+
2153+
### 12.1 概述
2154+
2155+
Platform Services 是与运行平台相关的服务,提供设备能力抽象,支持 Web 和 DWEB 两种运行环境。
2156+
2157+
**设计目标:**
2158+
- **编译时实现选择**:通过 Vite alias 实现完美 tree-shaking
2159+
- **统一 API**:上层代码无需关心运行环境
2160+
- **Mock 支持**:开发和测试时可使用 Mock 实现
2161+
2162+
### 12.2 服务列表
2163+
2164+
| 服务 | 说明 | Web 实现 | DWEB 实现 |
2165+
|-----|------|---------|----------|
2166+
| BiometricService | 生物识别认证 | WebAuthn API | @plaoc/plugins biometricsPlugin |
2167+
| SecureStorageService | 安全存储 | localStorage + AES | @plaoc/plugins keyValuePlugin |
2168+
| ClipboardService | 剪贴板操作 | Clipboard API | @plaoc/plugins clipboardPlugin |
2169+
| ToastService | 轻提示 | DOM 注入 | @plaoc/plugins toastPlugin |
2170+
| CameraService | 相机/扫码 | MediaDevices API | @plaoc/plugins barcodeScannerPlugin |
2171+
| HapticsService | 触觉反馈 | Vibration API | @plaoc/plugins hapticsPlugin |
2172+
2173+
### 12.3 目标架构(每服务独立文件夹)
2174+
2175+
```
2176+
src/services/
2177+
├── biometric/
2178+
│ ├── index.ts # 统一导出 + ServiceProvider
2179+
│ ├── types.ts # 接口定义
2180+
│ ├── web.ts # Web 实现
2181+
│ ├── dweb.ts # DWEB 实现
2182+
│ └── mock.ts # Mock 实现
2183+
2184+
├── storage/
2185+
│ ├── index.ts
2186+
│ ├── types.ts
2187+
│ ├── web.ts
2188+
│ ├── dweb.ts
2189+
│ └── mock.ts
2190+
2191+
├── clipboard/
2192+
│ ├── index.ts
2193+
│ ├── types.ts
2194+
│ ├── web.ts
2195+
│ ├── dweb.ts
2196+
│ └── mock.ts
2197+
2198+
├── toast/
2199+
│ ├── index.ts
2200+
│ ├── types.ts
2201+
│ ├── web.ts
2202+
│ ├── dweb.ts
2203+
│ └── mock.ts
2204+
2205+
├── camera/
2206+
│ ├── index.ts
2207+
│ ├── types.ts
2208+
│ ├── web.ts
2209+
│ ├── dweb.ts
2210+
│ └── mock.ts
2211+
2212+
├── haptics/
2213+
│ ├── index.ts
2214+
│ ├── types.ts
2215+
│ ├── web.ts
2216+
│ ├── dweb.ts
2217+
│ └── mock.ts
2218+
2219+
└── index.ts # 统一导出所有服务
2220+
```
2221+
2222+
### 12.4 接口定义示例
2223+
2224+
```typescript
2225+
// src/services/biometric/types.ts
2226+
2227+
export interface BiometricOptions {
2228+
reason?: string
2229+
fallbackToPassword?: boolean
2230+
}
2231+
2232+
export interface BiometricResult {
2233+
success: boolean
2234+
error?: string
2235+
}
2236+
2237+
export interface IBiometricService {
2238+
/** 检查是否可用 */
2239+
isAvailable(): Promise<boolean>
2240+
2241+
/** 执行认证 */
2242+
authenticate(options?: BiometricOptions): Promise<BiometricResult>
2243+
}
2244+
```
2245+
2246+
```typescript
2247+
// src/services/clipboard/types.ts
2248+
2249+
export interface IClipboardService {
2250+
/** 写入文本 */
2251+
writeText(text: string): Promise<void>
2252+
2253+
/** 读取文本 */
2254+
readText(): Promise<string>
2255+
}
2256+
```
2257+
2258+
```typescript
2259+
// src/services/toast/types.ts
2260+
2261+
export type ToastType = 'success' | 'error' | 'warning' | 'info'
2262+
2263+
export interface ToastOptions {
2264+
message: string
2265+
type?: ToastType
2266+
duration?: number
2267+
}
2268+
2269+
export interface IToastService {
2270+
/** 显示 toast */
2271+
show(options: ToastOptions): void
2272+
2273+
/** 快捷方法 */
2274+
success(message: string): void
2275+
error(message: string): void
2276+
warning(message: string): void
2277+
info(message: string): void
2278+
}
2279+
```
2280+
2281+
### 12.5 编译时实现选择
2282+
2283+
**Vite 配置:**
2284+
2285+
```typescript
2286+
// vite.config.ts
2287+
export default defineConfig({
2288+
resolve: {
2289+
alias: {
2290+
// 根据 SERVICE_IMPL 环境变量选择实现
2291+
'#biometric-impl': `./src/services/biometric/${process.env.SERVICE_IMPL || 'web'}.ts`,
2292+
'#storage-impl': `./src/services/storage/${process.env.SERVICE_IMPL || 'web'}.ts`,
2293+
'#clipboard-impl': `./src/services/clipboard/${process.env.SERVICE_IMPL || 'web'}.ts`,
2294+
'#toast-impl': `./src/services/toast/${process.env.SERVICE_IMPL || 'web'}.ts`,
2295+
'#camera-impl': `./src/services/camera/${process.env.SERVICE_IMPL || 'web'}.ts`,
2296+
'#haptics-impl': `./src/services/haptics/${process.env.SERVICE_IMPL || 'web'}.ts`,
2297+
}
2298+
}
2299+
})
2300+
```
2301+
2302+
**服务入口文件:**
2303+
2304+
```typescript
2305+
// src/services/biometric/index.ts
2306+
import type { IBiometricService } from './types'
2307+
import { BiometricService } from '#biometric-impl'
2308+
2309+
export type { IBiometricService, BiometricOptions, BiometricResult } from './types'
2310+
export { BiometricService }
2311+
2312+
// 创建单例
2313+
export const biometricService: IBiometricService = new BiometricService()
2314+
```
2315+
2316+
**构建脚本:**
2317+
2318+
```json
2319+
{
2320+
"scripts": {
2321+
"dev": "SERVICE_IMPL=web vite",
2322+
"dev:mock": "SERVICE_IMPL=mock vite",
2323+
"build:web": "SERVICE_IMPL=web vite build",
2324+
"build:dweb": "SERVICE_IMPL=dweb vite build"
2325+
}
2326+
}
2327+
```
2328+
2329+
### 12.6 React Hooks
2330+
2331+
```typescript
2332+
// src/services/hooks.ts
2333+
import { biometricService } from './biometric'
2334+
import { clipboardService } from './clipboard'
2335+
import { toastService } from './toast'
2336+
import { hapticsService } from './haptics'
2337+
2338+
export function useBiometric() {
2339+
return biometricService
2340+
}
2341+
2342+
export function useClipboard() {
2343+
return clipboardService
2344+
}
2345+
2346+
export function useToast() {
2347+
return toastService
2348+
}
2349+
2350+
export function useHaptics() {
2351+
return hapticsService
2352+
}
2353+
```
2354+
2355+
**使用示例:**
2356+
2357+
```typescript
2358+
function WalletAddress({ address }: { address: string }) {
2359+
const clipboard = useClipboard()
2360+
const toast = useToast()
2361+
const haptics = useHaptics()
2362+
2363+
const handleCopy = async () => {
2364+
await clipboard.writeText(address)
2365+
await haptics.impact('light')
2366+
toast.success('地址已复制')
2367+
}
2368+
2369+
return (
2370+
<button onClick={handleCopy}>
2371+
{address}
2372+
</button>
2373+
)
2374+
}
2375+
```
2376+
2377+
### 12.7 Mock 实现(测试用)
2378+
2379+
```typescript
2380+
// src/services/biometric/mock.ts
2381+
import type { IBiometricService, BiometricOptions, BiometricResult } from './types'
2382+
2383+
// 通过 window 暴露控制接口,供 E2E 测试使用
2384+
declare global {
2385+
interface Window {
2386+
__MOCK_BIOMETRIC__?: {
2387+
available: boolean
2388+
shouldSucceed: boolean
2389+
}
2390+
}
2391+
}
2392+
2393+
export class BiometricService implements IBiometricService {
2394+
async isAvailable(): Promise<boolean> {
2395+
return window.__MOCK_BIOMETRIC__?.available ?? true
2396+
}
2397+
2398+
async authenticate(options?: BiometricOptions): Promise<BiometricResult> {
2399+
const shouldSucceed = window.__MOCK_BIOMETRIC__?.shouldSucceed ?? true
2400+
return {
2401+
success: shouldSucceed,
2402+
error: shouldSucceed ? undefined : 'Mock authentication failed'
2403+
}
2404+
}
2405+
}
2406+
```
2407+
2408+
### 12.8 E2E 测试集成
2409+
2410+
```typescript
2411+
// e2e/services.spec.ts
2412+
import { test, expect } from '@playwright/test'
2413+
2414+
test('biometric authentication', async ({ page }) => {
2415+
// 配置 mock
2416+
await page.evaluate(() => {
2417+
window.__MOCK_BIOMETRIC__ = { available: true, shouldSucceed: true }
2418+
})
2419+
2420+
// 触发认证
2421+
await page.click('[data-testid="biometric-auth-button"]')
2422+
2423+
// 验证结果
2424+
await expect(page.locator('[data-testid="auth-success"]')).toBeVisible()
2425+
})
2426+
2427+
test('clipboard copy', async ({ page }) => {
2428+
await page.click('[data-testid="copy-address-button"]')
2429+
2430+
// 验证 toast 提示
2431+
await expect(page.locator('.toast-success')).toContainText('已复制')
2432+
2433+
// 验证剪贴板内容(通过 mock)
2434+
const clipboardContent = await page.evaluate(() => window.__CLIPBOARD__)
2435+
expect(clipboardContent).toBe('0x...')
2436+
})
2437+
```
2438+
2439+
---
2440+
2441+
*文档版本: 3.0 (强类型安全版 + Platform Services)*
21522442
*配套文档: PDR.md (产品需求) / TDD.md (技术设计)*

0 commit comments

Comments
 (0)