---
phase: 11-predictv3
plan: 03
type: execute
wave: 2
depends_on:
  - 11-01
  - 11-02
files_modified:
  - public/assets/js/backend/history.js
autonomous: true
requirements:
  - PRED-01
  - PRED-02
must_haves:
  truths:
    - "用户可以在预测弹窗中看到每个号码的置信度百分比"
    - "用户可以在回测结果区域看到 NDCG@5 和 MRR 指标"
    - "用户可以看到各排名位置的命中分布柱状图"
    - "用户可以看到数据不足时的警告提示"
  artifacts:
    - path: "public/assets/js/backend/history.js"
      provides: "置信度和新回测指标前端展示"
      contains: "renderPredict|confidence|ndcg_5|mrr|hit_distribution|data_warning"
  key_links:
    - from: "renderPredict"
      to: "backtest.ndcg_5, backtest.mrr, confidence, backtest.data_warning"
      via: "property access in rendering logic"
---

# Phase 11 - Plan 03: 前端展示优化

## Objective

更新前端 `renderPredict` 方法，展示新增的置信度指标和扩展的回测指标（NDCG、MRR、命中分布、数据警告）。

**Purpose:** 后端已计算置信度和新回测指标，前端需要将这些数据可视化呈现给用户，提升预测结果的可读性和决策辅助价值。

**Output:** `history.js` 中的 `renderPredict` 方法扩展，新增置信度展示区域和回测指标扩展展示。

## Tasks

### Task 1: 添加置信度展示区域（含数据警告提示）

<read_first>
- D:\code\php\amlhc\public\assets\js\backend\history.js (line 1700-1871, renderPredict 方法)
</read_first>

<action>
在 `renderPredict` 方法中，找到以下变量声明位置（约 line 1701）：

```javascript
var predictions = data.predictions || [];
var analysis = data.analysis || {};
var hitInfo = data.hit_info || null;
var actualResult = data.actual_result || null;
var backtest = data.backtest || null;
```

在 `backtest` 声明后添加 `confidence` 变量：

```javascript
var confidence = data.confidence || null;
```

然后在回测验证结果展示区域（约 line 1732-1751）之前，插入置信度展示区域：

在 line 1732 之前（即 `// 回测验证结果` 注释之前）插入：

```javascript
// 置信度评估展示（V2和V3版本）
if (confidence && (version === 'v2' || version === 'v3')) {
    html += '<div style="background:#fff8e1;border:1px solid #ffb300;border-radius:6px;padding:12px;margin-bottom:15px;">';
    html += '<div style="font-size:13px;font-weight:bold;color:#ff8f00;margin-bottom:8px;"><i class="fa fa-star-half-o"></i> 预测置信度评估</div>';
    
    // 数据警告提示（数据不足时显示）
    if (confidence.data_warning) {
        html += '<div style="font-size:11px;color:#d32f2f;background:#ffebee;padding:6px;border-radius:4px;margin-bottom:8px;"><i class="fa fa-exclamation-triangle"></i> ' + confidence.data_warning + '</div>';
    }
    
    html += '<div style="display:flex;gap:20px;align-items:center;">';
    html += '<div style="text-align:center;"><div style="font-size:24px;font-weight:bold;color:#ff8f00;">' + confidence.overall_confidence + '%</div><div style="font-size:12px;color:#666;">整体置信度</div></div>';
    
    // 各排名置信度（使用得分集中度维度）
    if (confidence.confidence_scores && confidence.confidence_scores.length > 0) {
        html += '<div style="display:flex;gap:8px;">';
        for (var i = 0; i < confidence.confidence_scores.length; i++) {
            var cs = confidence.confidence_scores[i];
            // 阈值定义：>=70%高(绿)、50-70%中(橙)、<50%低(红)
            var confLevel = cs.confidence >= 70 ? '高' : (cs.confidence >= 50 ? '中' : '低');
            var confColor = cs.confidence >= 70 ? '#4caf50' : (cs.confidence >= 50 ? '#ff9800' : '#f44336');
            html += '<div style="text-align:center;padding:5px;background:#fff;border-radius:4px;">';
            html += '<div style="font-size:14px;font-weight:bold;color:' + confColor + ';">' + cs.confidence + '%</div>';
            html += '<div style="font-size:10px;color:#999;">#' + cs.rank + '</div>';
            html += '</div>';
        }
        html += '</div>';
    }
    html += '</div></div>';
}
```

实现要点：
- 整体置信度以大数字展示，各排名置信度以小卡片形式横向排列
- 置信度分三级：高(>=70%, 绿色)、中(50-70%, 橙色)、低(<50%, 红色)
- 只在 V2 和 V3 版本中显示
- 新增 data_warning 展示：数据不足时显示红色警告提示
</action>

<acceptance_criteria>
- grep 匹配: `confidence.overall_confidence` 在 history.js renderPredict 方法中存在
- grep 匹配: `confidence.confidence_scores` 在 history.js renderPredict 方法中存在
- grep 匹配: `confidence.data_warning` 在 history.js renderPredict 方法中存在
- grep 匹配: `confLevel` 和 `confColor` 变量在 history.js 中存在
- 置信度展示区域在回测验证结果之前显示
</acceptance_criteria>

### Task 2: 扩展回测指标展示区域（含数据警告和命中分布柱状图）

<read_first>
- D:\code\php\amlhc\public\assets\js\backend\history.js (line 1732-1751, 回测验证结果展示区域)
</read_first>

<action>
在 `renderPredict` 方法中，找到回测验证结果展示区域（约 line 1732-1751），现有代码展示命中率、命中次数、平均排名三个指标。

找到以下代码段：

```javascript
html += '<div style="display:flex;gap:20px;">';
html += '<div style="text-align:center;"><div style="font-size:24px;font-weight:bold;color:#2196f3;">' + backtest.hit_rate + '%</div><div style="font-size:12px;color:#666;">命中率（Top5）</div></div>';
html += '<div style="text-align:center;"><div style="font-size:24px;font-weight:bold;color:#4caf50;">' + backtest.total_hits + '/' + backtest.total_tests + '</div><div style="font-size:12px;color:#666;">命中次数</div></div>';
html += '<div style="text-align:center;"><div style="font-size:24px;font-weight:bold;color:#ff9800;">' + (backtest.avg_rank || '—') + '</div><div style="font-size:12px;color:#666;">平均排名</div></div>';
html += '</div>';
```

替换为：

```javascript
// 回测数据警告提示
if (backtest.data_warning) {
    html += '<div style="font-size:11px;color:#d32f2f;background:#ffebee;padding:6px;border-radius:4px;margin-bottom:8px;"><i class="fa fa-exclamation-triangle"></i> ' + backtest.data_warning + '</div>';
}

html += '<div style="display:flex;gap:15px;flex-wrap:wrap;">';
html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#2196f3;">' + backtest.hit_rate + '%</div><div style="font-size:11px;color:#666;">命中率（Top5）</div></div>';
html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#4caf50;">' + backtest.total_hits + '/' + backtest.total_tests + '</div><div style="font-size:11px;color:#666;">命中次数</div></div>';
html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#ff9800;">' + (backtest.avg_rank || '—') + '</div><div style="font-size:11px;color:#666;">平均排名</div></div>';

// 新增指标：NDCG@5 和 MRR（百分比展示）
if (backtest.ndcg_5 !== undefined) {
    html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#9c27b0;">' + (backtest.ndcg_5 * 100).toFixed(1) + '%</div><div style="font-size:11px;color:#666;">NDCG@5</div></div>';
}
if (backtest.mrr !== undefined) {
    html += '<div style="font-size:22px;font-weight:bold;color:#00bcd4;">' + (backtest.mrr * 100).toFixed(1) + '%</div><div style="font-size:11px;color:#666;">MRR</div></div>';
}

// 转移概率阶数显示（来自11-05的transition_order字段）
if (analysis && analysis.transition_order !== undefined) {
    html += '<div style="text-align:center;padding:8px;"><div style="font-size:22px;font-weight:bold;color:#607d8b;">' + analysis.transition_order + '阶</div><div style="font-size:11px;color:#666;">转移概率</div></div>';
}
html += '</div>';

// 命中分布柱状图（使用rank_1..rank_5键名）
if (backtest.hit_distribution && Object.keys(backtest.hit_distribution).length > 0) {
    var distribution = backtest.hit_distribution;
    var maxHit = 0;
    // 找最大值用于计算柱状图高度比例
    for (var r = 1; r <= 5; r++) {
        var key = 'rank_' + r;
        if (distribution[key] > maxHit) {
            maxHit = distribution[key];
        }
    }
    
    html += '<div style="margin-top:10px;font-size:11px;color:#666;">命中分布（各排名命中次数）：</div>';
    html += '<div style="display:flex;gap:8px;align-items:flex-end;height:60px;margin-top:5px;padding:5px;background:#f5f5f5;border-radius:4px;">';
    for (var r = 1; r <= 5; r++) {
        var key = 'rank_' + r;
        var hitCount = distribution[key] || 0;
        var barHeight = maxHit > 0 ? (hitCount / maxHit * 45) : 0;
        var barColor = hitCount > 0 ? '#4caf50' : '#e0e0e0';
        html += '<div style="text-align:center;min-width:50px;">';
        html += '<div style="height:' + barHeight + 'px;background:' + barColor + ';border-radius:2px 2px 0 0;width:35px;margin:0 auto;"></div>';
        html += '<div style="font-size:10px;color:#666;margin-top:2px;">#' + r + '</div>';
        html += '<div style="font-size:11px;color:#333;font-weight:bold;">' + hitCount + '</div>';
        html += '</div>';
    }
    html += '</div>';
}
```

实现要点：
- NDCG@5 和 MRR 以百分比形式展示（乘100后保留1位小数）
- 命中分布以简单柱状图展示，排名1-5横向排列
- 柱状图高度按最大命中次数比例计算
- 使用 rank_1..rank_5 键名格式解析分布数据
- 新增 data_warning 展示：回测数据不足时显示警告
- 转移概率阶数从 analysis.transition_order 获取（而非 backtest）
</action>

<acceptance_criteria>
- grep 匹配: `backtest.ndcg_5` 在 history.js renderPredict 方法中存在
- grep 匹配: `backtest.mrr` 在 history.js renderPredict 方法中存在
- grep 匹配: `backtest.hit_distribution` 在 history.js renderPredict 方法中存在
- grep 匹配: `backtest.data_warning` 在 history.js renderPredict 方法中存在
- grep 匹配: `rank_1|rank_2|rank_3|rank_4|rank_5` 在命中分布解析中存在
- grep 匹配: `analysis.transition_order` 在 history.js renderPredict 方法中存在
- 命中分布柱状图使用 div 元素实现
</acceptance_criteria>

### Task 3: 在预测号码卡片中显示置信度（含得分集中度展示）

<read_first>
- D:\code\php\amlhc\public\assets\js\backend\history.js (line 1828-1866, 预测号码列表渲染)
</read_first>

<action>
在预测号码列表渲染区域（约 line 1828-1866），找到号码卡片渲染代码：

在现有得分显示后添加置信度显示。找到以下代码段：

```javascript
html += '<div style="font-size:11px;color:#2e7d32;font-weight:bold;">得分:' + p.score + '</div>';
```

替换为：

```javascript
html += '<div style="font-size:11px;color:#2e7d32;font-weight:bold;">得分:' + p.score + '</div>';

// 显示置信度（V3版本）
if (version === 'v3' && confidence && confidence.confidence_scores) {
    var csForNum = confidence.confidence_scores.find(function(c) { return c.num === p.num; });
    if (csForNum) {
        // 阈值定义：>=70%高(绿)、50-70%中(橙)、<50%低(红)
        var confLevel = csForNum.confidence >= 70 ? '高' : (csForNum.confidence >= 50 ? '中' : '低');
        var confColor = csForNum.confidence >= 70 ? '#4caf50' : (csForNum.confidence >= 50 ? '#ff9800' : '#f44336');
        html += '<div style="font-size:10px;"><span style="color:' + confColor + ';font-weight:bold;">置信度:' + confLevel + '</span> <span style="color:#666;">(' + csForNum.confidence + '%)</span></div>';
    }
}
```

实现要点：
- 在号码卡片中显示置信度等级（高/中/低）和具体百分比
- 使用与整体置信度展示相同的颜色映射阈值
- 只在 V3 版本中显示
- 置信度字段使用 score_concentration（得分集中度）维度
</action>

<acceptance_criteria>
- grep 匹配: `csForNum` 变量在 history.js 中存在
- grep 匹配: `置信度:` 在号码卡片渲染代码中存在
- 置信度显示在得分下方
- grep 匹配: `csForNum.confidence` 在号码卡片渲染中存在
</acceptance_criteria>

## Verification

1. 打开 history 页面，点击"智能预测"按钮
2. 使用 V3 版本执行预测，验证：
   - 置信度评估区域是否显示
   - 数据不足时是否显示警告提示
   - 回测结果中是否显示 NDCG@5、MRR、命中分布柱状图
   - 预测号码卡片中是否显示置信度等级和百分比

## Success Criteria

1. `renderPredict` 方法已更新，新增置信度展示区域
2. 回测结果展示区域已扩展，包含 NDCG@5、MRR、命中分布、数据警告
3. 预测号码卡片显示置信度等级和百分比
4. 命中分布使用 rank_1..rank_5 键名格式解析
5. 所有新增展示样式与原有 UI 风格一致
6. 数据警告以红色背景突出显示

## Output

完成后创建 `.planning/phases/11-predictv3/11-03-SUMMARY.md`