feat(dashboard): 添加特码热力图功能

在控制台增加特码热力图可视化功能:
- 新增 getSpecialHeatmap() 方法生成热力图数据
- 热力图展示近N期特码号码分布(X轴期号,Y轴号码1-49)
- 使用号码波色作为单元格颜色,直观展示开奖规律
This commit is contained in:
2026-04-22 22:41:02 +08:00
parent 54dd2fe5ad
commit f4c67bd102
6 changed files with 245 additions and 3 deletions
+7 -1
View File
@@ -28,7 +28,7 @@ See: .planning/PROJECT.md (updated 2026-04-21)
Phase: 01 (omitted-number-analysis) — COMPLETE Phase: 01 (omitted-number-analysis) — COMPLETE
Plan: 3 of 3 Plan: 3 of 3
Status: Phase 1 complete, ready to plan next phase Status: Phase 1 complete, ready to plan next phase
Last activity: 2026-04-21 -- Phase 01 complete, Phases 2-10 added Last activity: 2026-04-22 -- Completed quick task 260422-vep: 特码热力图功能
Progress: [████░░░░░░] 10% Progress: [████░░░░░░] 10%
@@ -69,6 +69,12 @@ None yet.
None yet. None yet.
### Quick Tasks Completed
| # | Description | Date | Commit | Directory |
|---|-------------|------|--------|-----------|
| 260422-vep | 在控制台增加特码热力图功能 | 2026-04-22 | 73e7403 | [260422-vep](./quick/260422-vep/) |
## Deferred Items ## Deferred Items
| Category | Item | Status | Deferred At | | Category | Item | Status | Deferred At |
@@ -0,0 +1,48 @@
---
quick_id: 260422-vep
description: 在控制台增加特码热力图功能
mode: quick
date: 2026-04-22
---
# Quick Task Plan: 特码热力图功能
## Goal
在控制台(Dashboard)增加特码热力图功能,直观展示特码号码在近 N 期内的出现分布情况。
## Implementation Design
### 热力图数据结构
- X轴:期号(最近30期)
- Y轴:号码 1-49
- 颜色:基于号码的波色(红/蓝/绿),出现时显示颜色,未出现时显示浅灰
### Files to Modify
1. `application/admin/model/History.php` - 添加 `getSpecialHeatmap()` 方法
2. `application/admin/controller/History.php` - 添加 `specialHeatmap` API 接口和权限声明
3. `public/assets/js/backend/dashboard.js` - 添加热力图渲染逻辑
4. `application/admin/view/dashboard/index.html` - 无需修改(热力图容器由 JS 动态生成)
## Tasks
### Task 1: 后端数据模型
**File:** `application/admin/model/History.php`
**Action:** 添加 `getSpecialHeatmap()` 方法
**Verify:** 方法返回热力图数据(expects, heatmap_data, colorMap
**Done:** 方法可被正确调用并返回结构化数据
### Task 2: API 接口
**File:** `application/admin/controller/History.php`
**Action:**
1.`$noNeedRight` 数组添加 `specialHeatmap`
2. 添加 `specialHeatmap()` 方法
**Verify:** API 可通过 AJAX 访问并返回正确数据
**Done:** 接口可被前端调用
### Task 3: 前端渲染
**File:** `public/assets/js/backend/dashboard.js`
**Action:**
1. 在 AJAX 请求中添加热力图数据获取
2. 在 render 函数中添加热力图 HTML 和 ECharts 渲染
**Verify:** 热力图正确显示在 Dashboard 页面
**Done:** 热力图可视化完成
@@ -0,0 +1,34 @@
# Quick Task 260422-vep: 特码热力图功能 - Summary
**Status:** Completed
**Date:** 2026-04-22
## Implementation Summary
在控制台(Dashboard)增加了特码热力图功能,直观展示特码号码在近 N 期内的出现分布情况。
### Changes Made
**1. Backend Model (`application/admin/model/History.php`)**
- 新增 `getSpecialHeatmap($periods)` 方法
- 返回热力图数据结构:expects(期号列表)、heatmap(出现数据)、colors(号码波色)、nums(号码列表)
**2. Backend Controller (`application/admin/controller/History.php`)**
-`$noNeedRight` 数组添加 `specialHeatmap` 权限声明
- 新增 `specialHeatmap()` API 接口方法
- `getDashboardData()` 方法已自动包含热力图数据
**3. Frontend JS (`public/assets/js/backend/dashboard.js`)**
-`render()` 函数中添加热力图 HTML 部分
- 新增 ECharts 热力图渲染逻辑,使用号码波色作为单元格颜色
### Features
- X轴:期号(从左往右,从远到近)
- Y轴:号码 1-49
- 颜色:号码对应波色(红/蓝/绿),未开出显示浅灰
- Tooltip:显示期号、号码、状态
### Files Modified
1. `application/admin/model/History.php` - 添加热力图数据方法
2. `application/admin/controller/History.php` - 添加 API 接口
3. `public/assets/js/backend/dashboard.js` - 添加前端渲染逻辑
+16 -1
View File
@@ -22,7 +22,7 @@ class History extends Backend
* 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录) * 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录)
* @var array * @var array
*/ */
protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers', 'colorWaveAnalysis', 'zodiacAnalysis', 'oddEvenAnalysis', 'bigSmallAnalysis', 'specialTrend', 'consecutiveNumbers', 'tailNumbers', 'dashboard']; protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers', 'colorWaveAnalysis', 'zodiacAnalysis', 'oddEvenAnalysis', 'bigSmallAnalysis', 'specialTrend', 'consecutiveNumbers', 'tailNumbers', 'dashboard', 'specialHeatmap'];
public function _initialize() public function _initialize()
{ {
@@ -236,5 +236,20 @@ class History extends Backend
} }
} }
/**
* 特码热力图
*/
public function specialHeatmap()
{
if ($this->request->isAjax()) {
$periods = $this->request->get('periods', 30, 'intval');
if ($periods < 10 || $periods > 100) {
$this->error('期数范围必须在 10-100 之间');
}
$result = $this->model->getSpecialHeatmap($periods);
$this->success('查询成功', null, $result);
}
}
} }
+64 -1
View File
@@ -463,7 +463,70 @@ class History extends Model
'oddeven' => $this->getOddEvenAnalysis($periods, 'special'), 'oddeven' => $this->getOddEvenAnalysis($periods, 'special'),
'bigsmall' => $this->getBigSmallAnalysis($periods, 'special'), 'bigsmall' => $this->getBigSmallAnalysis($periods, 'special'),
'special' => $this->getSpecialTrend($periods), 'special' => $this->getSpecialTrend($periods),
'tailnumbers' => $this->getTailNumbers($periods, 'special') 'tailnumbers' => $this->getTailNumbers($periods, 'special'),
'heatmap' => $this->getSpecialHeatmap($periods)
];
}
/**
* 特码热力图数据
* @param int $periods 查询最近多少期
* @return array {expects: [], heatmap: [[x, y, value]], colors: [号码对应颜色]}
*/
public function getSpecialHeatmap($periods = 30)
{
$num_model = new Num();
$colorMap = $num_model->column('color', 'num');
// 查询最近 N 期特码数据
$history = $this
->field('expect,num7,openTime')
->order('openTime', 'desc')
->limit($periods)
->select();
if (empty($history)) {
return ['expects' => [], 'heatmap' => [], 'colors' => []];
}
// 反转数组,使数据从左到右为从远到近
$history = array_reverse($history);
$expects = [];
$heatmap = [];
// 构建热力图数据:[x_index, y_index, value]
// x_index = 期号索引(0到periods-1
// y_index = 号码-10到48,号码1-49
// value = 1(出现)或 0(未出现)
foreach ($history as $idx => $row) {
$expects[] = (string)$row['expect'];
$specialNum = (int)$row['num7'];
// 标记该期特码号码出现
if ($specialNum >= 1 && $specialNum <= 49) {
$heatmap[] = [$idx, $specialNum - 1, 1];
}
}
// 补充号码颜色映射(索引0对应号码1)
$colors = [];
for ($num = 1; $num <= 49; $num++) {
$color = $colorMap[$num] ?? '';
if (strpos($color, '红') !== false) {
$colors[] = '#e74c3c';
} elseif (strpos($color, '蓝') !== false) {
$colors[] = '#3498db';
} elseif (strpos($color, '绿') !== false) {
$colors[] = '#2ecc71';
} else {
$colors[] = '#95a5a6';
}
}
return [
'expects' => $expects,
'heatmap' => $heatmap,
'colors' => $colors,
'nums' => range(1, 49) // 号码列表
]; ];
} }
+76
View File
@@ -63,6 +63,10 @@ define(['jquery'], function ($) {
html += '<div class="dash-section"><h4>🔢 尾数频率</h4><div id="tail-chart" style="width:100%;height:220px;"></div></div>'; html += '<div class="dash-section"><h4>🔢 尾数频率</h4><div id="tail-chart" style="width:100%;height:220px;"></div></div>';
// 热力图部分
var hm = data.heatmap;
html += '<div class="dash-section"><h4>🎨 特码热力图</h4><div style="font-size:12px;color:#999;margin-bottom:8px;">X轴:期号(从左往右,从远到近) | Y轴:号码1-49 | 颜色:号码波色</div><div id="heatmap-chart" style="width:100%;height:500px;"></div></div>';
$('#dash-content').html(html); $('#dash-content').html(html);
// 波色饼图 // 波色饼图
@@ -185,6 +189,78 @@ define(['jquery'], function ($) {
$(window).on('resize', function(){ tChart.resize(); }); $(window).on('resize', function(){ tChart.resize(); });
} }
} }
// 特码热力图
if (typeof echarts !== 'undefined' && hm && hm.expects && hm.expects.length > 0) {
var hmDom = document.getElementById('heatmap-chart');
if (hmDom) {
var hmChart = echarts.init(hmDom);
// 构建完整的热力图数据矩阵(每期每号码)
var hmData = [];
for (var x = 0; x < hm.expects.length; x++) {
for (var y = 0; y < 49; y++) {
// 检查该期是否有该号码作为特码
var found = false;
for (var i = 0; i < hm.heatmap.length; i++) {
if (hm.heatmap[i][0] === x && hm.heatmap[i][1] === y) {
found = true;
break;
}
}
hmData.push([x, y, found ? 1 : 0]);
}
}
hmChart.setOption({
tooltip: {
position: 'top',
formatter: function(p) {
var periodIdx = p.data[0];
var num = p.data[1] + 1;
var val = p.data[2];
return '期号: ' + hm.expects[periodIdx] + '<br/>号码: ' + num + '<br/>状态: ' + (val === 1 ? '已开出' : '未开出');
}
},
grid: {left: 80, right: 30, bottom: 60, top: 30},
xAxis: {
type: 'category',
data: hm.expects,
axisLabel: {rotate: 45, fontSize: 10, interval: 0},
splitArea: {show: true}
},
yAxis: {
type: 'category',
data: hm.nums,
axisLabel: {fontSize: 10},
splitArea: {show: true}
},
visualMap: {
min: 0,
max: 1,
show: false,
inRange: {
color: ['#f0f0f0', '#e74c3c']
}
},
series: [{
type: 'heatmap',
data: hmData,
label: {show: false},
itemStyle: {
color: function(p) {
// 未开出显示浅灰,开出显示该号码的波色
if (p.data[2] === 0) return '#f5f5f5';
var numIdx = p.data[1];
return hm.colors[numIdx] || '#95a5a6';
}
},
emphasis: {
itemStyle: {shadowBlur: 10, shadowColor: 'rgba(0,0,0,0.5)'}
}
}]
});
$(window).on('resize', function(){ hmChart.resize(); });
}
}
} }
$('#btn-dash-refresh').on('click', loadData); $('#btn-dash-refresh').on('click', loadData);