feat(dashboard): 添加特码热力图功能
在控制台增加特码热力图可视化功能: - 新增 getSpecialHeatmap() 方法生成热力图数据 - 热力图展示近N期特码号码分布(X轴期号,Y轴号码1-49) - 使用号码波色作为单元格颜色,直观展示开奖规律
This commit is contained in:
+7
-1
@@ -28,7 +28,7 @@ See: .planning/PROJECT.md (updated 2026-04-21)
|
||||
Phase: 01 (omitted-number-analysis) — COMPLETE
|
||||
Plan: 3 of 3
|
||||
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%
|
||||
|
||||
@@ -69,6 +69,12 @@ None yet.
|
||||
|
||||
None yet.
|
||||
|
||||
### Quick Tasks Completed
|
||||
|
||||
| # | Description | Date | Commit | Directory |
|
||||
|---|-------------|------|--------|-----------|
|
||||
| 260422-vep | 在控制台增加特码热力图功能 | 2026-04-22 | 73e7403 | [260422-vep](./quick/260422-vep/) |
|
||||
|
||||
## Deferred Items
|
||||
|
||||
| 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` - 添加前端渲染逻辑
|
||||
@@ -22,7 +22,7 @@ class History extends Backend
|
||||
* 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录)
|
||||
* @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()
|
||||
{
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -463,7 +463,70 @@ class History extends Model
|
||||
'oddeven' => $this->getOddEvenAnalysis($periods, 'special'),
|
||||
'bigsmall' => $this->getBigSmallAnalysis($periods, 'special'),
|
||||
'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 = 号码-1(0到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) // 号码列表
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -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>';
|
||||
|
||||
// 热力图部分
|
||||
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);
|
||||
|
||||
// 波色饼图
|
||||
@@ -185,6 +189,78 @@ define(['jquery'], function ($) {
|
||||
$(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);
|
||||
|
||||
Reference in New Issue
Block a user