# Mock 数据规范

> 跨框架通用的 Mock 数据设计标准，确保设计稿还原和开发阶段使用动态数据
> 版本: v1.0.0 | 更新: 2026-03-31

---

## 🎯 核心原则

1. **动态优于静态** — 设计稿还原使用动态 mock 数据，禁止硬编码假字符串
2. **结构一致** — Mock 数据结构必须与真实 API 返回格式完全一致
3. **边界覆盖** — Mock 数据必须涵盖空列表、超长文本、多语言等边界场景
4. **易于切换** — Mock 与真实 API 可通过配置一键切换

---

## ⚡ Flutter Mock 数据模式

### Model.mock() 工厂模式（推荐）

```dart
class TransactionModel {
  final String id;
  final String title;
  final double amount;
  final DateTime createdAt;

  const TransactionModel({
    required this.id,
    required this.title,
    required this.amount,
    required this.createdAt,
  });

  /// 生成单个 mock 实例
  factory TransactionModel.mock({int index = 0}) {
    return TransactionModel(
      id: 'TXN${index.toString().padLeft(6, '0')}',
      title: '交易 #${index + 1}',
      amount: (index + 1) * 12.5,
      createdAt: DateTime.now().subtract(Duration(hours: index)),
    );
  }

  /// 生成 mock 列表
  static List<TransactionModel> mockList({int count = 10}) {
    return List.generate(count, (i) => TransactionModel.mock(index: i));
  }
}
```

### 在页面中使用

```dart
// ✅ 正确 — 使用 mock 工厂
final items = TransactionModel.mockList(count: 5);

// ❌ 禁止 — 硬编码假数据
final items = [
  {'title': '张三', 'amount': '100.00'},
  {'title': '李四', 'amount': '200.00'},
];
```

### 设计稿还原场景

```dart
class _DesignPreviewState extends State<DesignPreview> {
  // 使用 mock 数据展示 UI
  final _transactions = TransactionModel.mockList(count: 3);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: _transactions.length,
      itemBuilder: (_, i) => TransactionCard(data: _transactions[i]),
    );
  }
}
```

---

## ⚡ Vue Mock 数据模式

> 完整 Vue Mock 体系见 `get_standard_by_id({ id: "vue-api-mock-layer" })`

### 快速 Mock 工厂

```typescript
interface Order {
  id: string
  customerName: string
  amount: number
  status: 'pending' | 'completed' | 'cancelled'
}

function createMockOrder(overrides: Partial<Order> = {}): Order {
  return {
    id: `ORD${Math.random().toString(36).slice(2, 8)}`,
    customerName: `客户 ${Math.floor(Math.random() * 100)}`,
    amount: Math.floor(Math.random() * 10000) / 100,
    status: 'pending',
    ...overrides,
  }
}

function createMockOrders(count = 10): Order[] {
  return Array.from({ length: count }, (_, i) => createMockOrder())
}
```

---

## 📋 边界场景 Mock 清单

**设计稿还原时必须验证：**

| 场景 | Mock 数据 | 验证目的 |
|------|----------|---------|
| 空列表 | `mockList(count: 0)` | 空状态 UI 展示 |
| 单条数据 | `mockList(count: 1)` | 边界布局 |
| 超长文本 | `title: 'A' * 100` | 文本截断/换行 |
| 超长数字 | `amount: 99999999.99` | 数字格式化 |
| 多语言 | 中文 + 英文 mock | 不同字符宽度适配 |
| 图片缺失 | `imageUrl: null` | 占位图展示 |
| 加载状态 | `isLoading: true` | 骨架屏/loading |

---

## 🚫 反面模式

```dart
// ❌ 1. 直接在 UI 中硬编码字符串
Text('张三')
Text('¥100.00')
Text('2026-01-01')

// ❌ 2. 使用无类型的 Map
final data = {'name': '张三', 'amount': 100};

// ❌ 3. Mock 数据结构与 API 不一致
// API 返回 {data: {items: [...]}}  但 mock 直接返回 [...]

// ✅ 正确做法
Text(item.customerName)
Text(formatCurrency(item.amount))
Text(formatDate(item.createdAt))
```

---

## ✅ Mock 切换策略

### Flutter

```dart
// 环境配置
const bool kUseMockData = bool.fromEnvironment('USE_MOCK', defaultValue: false);

// Repository 层切换
class TransactionRepository {
  Future<List<TransactionModel>> getTransactions() async {
    if (kUseMockData) {
      return TransactionModel.mockList();
    }
    return _api.fetchTransactions();
  }
}
```

### Vue

```typescript
// .env.development
VITE_MOCK_ENABLED=true

// api 层
const useMock = import.meta.env.VITE_MOCK_ENABLED === 'true'
```
