feat: add comprehensive dashboard showing all analysis metrics in one view
This commit is contained in:
@@ -22,7 +22,7 @@ class History extends Backend
|
||||
* 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录)
|
||||
* @var array
|
||||
*/
|
||||
protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers', 'colorWaveAnalysis', 'zodiacAnalysis', 'oddEvenAnalysis', 'bigSmallAnalysis', 'sumAnalysis', 'consecutiveNumbers', 'tailNumbers'];
|
||||
protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers', 'colorWaveAnalysis', 'zodiacAnalysis', 'oddEvenAnalysis', 'bigSmallAnalysis', 'sumAnalysis', 'consecutiveNumbers', 'tailNumbers', 'dashboard'];
|
||||
|
||||
public function _initialize()
|
||||
{
|
||||
@@ -217,5 +217,24 @@ class History extends Backend
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 综合统计面板
|
||||
*/
|
||||
public function dashboard()
|
||||
{
|
||||
if ($this->request->isAjax()) {
|
||||
$periods = $this->request->get('periods', 30, 'intval');
|
||||
if ($periods < 10 || $periods > 100) {
|
||||
$this->error('期数范围必须在 10-100 之间');
|
||||
}
|
||||
$type = $this->request->get('type', 'all');
|
||||
if (!in_array($type, ['all', 'special'])) {
|
||||
$this->error('查询类型不正确');
|
||||
}
|
||||
$result = $this->model->getDashboardData($periods, $type);
|
||||
$this->success('查询成功', null, $result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,4 +25,5 @@ return [
|
||||
'Sum Chart' => '和值分析',
|
||||
'Consecutive' => '连号分析',
|
||||
'Tail Numbers' => '尾数分析',
|
||||
'Dashboard' => '综合统计面板',
|
||||
];
|
||||
|
||||
@@ -446,5 +446,21 @@ class History extends Model
|
||||
return ['all' => $all];
|
||||
}
|
||||
|
||||
/**
|
||||
* 综合统计面板
|
||||
*/
|
||||
public function getDashboardData($periods = 30, $type = 'all')
|
||||
{
|
||||
return [
|
||||
'hotcold' => $this->getHotColdNumbers($periods, $type),
|
||||
'colorwave' => $this->getColorWaveAnalysis($periods, $type),
|
||||
'zodiac' => $this->getZodiacAnalysis($periods, $type),
|
||||
'oddeven' => $this->getOddEvenAnalysis($periods, $type),
|
||||
'bigsmall' => $this->getBigSmallAnalysis($periods, $type),
|
||||
'sum' => $this->getSumAnalysis($periods),
|
||||
'tailnumbers' => $this->getTailNumbers($periods, $type)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<a href="javascript:;" class="btn btn-primary btn-sumchart" title="{:__('Sum Chart')}"><i class="fa fa-line-chart"></i> {:__('Sum Chart')}</a>
|
||||
<a href="javascript:;" class="btn btn-dark btn-consecutive" title="{:__('Consecutive')}"><i class="fa fa-link"></i> {:__('Consecutive')}</a>
|
||||
<a href="javascript:;" class="btn btn-default btn-tailnums" title="{:__('Tail Numbers')}"><i class="fa fa-list-ol"></i> {:__('Tail Numbers')}</a>
|
||||
<a href="javascript:;" class="btn btn-success btn-dashboard" title="{:__('Dashboard')}"><i class="fa fa-tachometer"></i> {:__('Dashboard')}</a>
|
||||
<!-- <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('history/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>-->
|
||||
</div>
|
||||
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
|
||||
|
||||
@@ -86,6 +86,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
|
||||
$(document).off('click', '.btn-tailnums').on('click', '.btn-tailnums', function () {
|
||||
Controller.api.showAnalysisDialog('tailNumbers');
|
||||
});
|
||||
|
||||
// 综合统计面板按钮事件
|
||||
$(document).off('click', '.btn-dashboard').on('click', '.btn-dashboard', function () {
|
||||
Controller.api.showDashboard();
|
||||
});
|
||||
},
|
||||
add: function () {
|
||||
Controller.api.bindevent();
|
||||
@@ -904,6 +909,143 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
|
||||
}
|
||||
html += '</div></div>';
|
||||
$('#consecutive-result', layero).html(html);
|
||||
},
|
||||
|
||||
/**
|
||||
* 综合统计面板
|
||||
*/
|
||||
showDashboard: function () {
|
||||
var html = '<div style="padding:20px;">' +
|
||||
'<div class="form-group">' +
|
||||
' <label>' + __('Query Periods') + ':</label>' +
|
||||
' <input type="number" id="dash-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
|
||||
' <button class="btn btn-primary" id="btn-dash-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
|
||||
'</div>' +
|
||||
'<div id="dash-result" style="margin-top:15px;"></div>' +
|
||||
'</div>';
|
||||
|
||||
Layer.open({
|
||||
type: 1,
|
||||
title: __('Dashboard'),
|
||||
area: ['90%', '90%'],
|
||||
content: html,
|
||||
shadeClose: false,
|
||||
maxmin: true,
|
||||
success: function (layero, index) {
|
||||
$('#btn-dash-query', layero).on('click', function () {
|
||||
var periods = parseInt($('#dash-periods', layero).val()) || 30;
|
||||
Controller.api.queryDashboard(periods, layero);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
queryDashboard: function (periods, layero) {
|
||||
var $btn = $('#btn-dash-query', layero);
|
||||
$btn.prop('disabled', true);
|
||||
$('#dash-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
|
||||
$.ajax({
|
||||
url: 'history/dashboard',
|
||||
type: 'GET',
|
||||
data: {periods: periods},
|
||||
dataType: 'json',
|
||||
success: function (ret) {
|
||||
if (ret.code == 1) {
|
||||
Controller.api.renderDashboard(ret.data, layero);
|
||||
} else {
|
||||
$('#dash-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
$('#dash-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
|
||||
},
|
||||
complete: function () {
|
||||
$btn.prop('disabled', false);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
renderDashboard: function (data, layero) {
|
||||
var getColor = function (color) {
|
||||
if (!color) return '#95a5a6';
|
||||
if (color.indexOf('红') !== -1) return '#e74c3c';
|
||||
if (color.indexOf('蓝') !== -1) return '#3498db';
|
||||
if (color.indexOf('绿') !== -1) return '#2ecc71';
|
||||
return '#95a5a6';
|
||||
};
|
||||
|
||||
var hc = data.hotcold;
|
||||
var cw = data.colorwave;
|
||||
var zo = data.zodiac;
|
||||
var oe = data.oddeven;
|
||||
var bs = data.bigsmall;
|
||||
var sm = data.sum;
|
||||
var tn = data.tailnumbers;
|
||||
|
||||
var ballHtml = function (item) {
|
||||
return '<div style="text-align:center;display:inline-block;margin:3px;"><span style="display:inline-block;width:28px;height:28px;line-height:28px;text-align:center;border-radius:50%;color:#fff;background-color:' + getColor(item.color) + ';font-weight:bold;font-size:12px;">' + item.num + '</span><div style="font-size:9px;color:#666;">' + item.count + '</div></div>';
|
||||
};
|
||||
|
||||
var html = '<div style="padding:10px;max-height:75vh;overflow-y:auto;">';
|
||||
|
||||
// 冷热号码
|
||||
html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">🔥❄️ 冷热号码</h4>';
|
||||
html += '<div style="display:flex;"><div style="flex:1;padding:5px;"><b style="color:#e74c3c;">热号 Top5</b><div>';
|
||||
for (var i = 0; i < 5; i++) html += ballHtml(hc.hot[i]);
|
||||
html += '</div></div><div style="flex:1;padding:5px;"><b style="color:#3498db;">冷号 Top5</b><div>';
|
||||
for (var i = 0; i < 5; i++) html += ballHtml(hc.cold[i]);
|
||||
html += '</div></div></div>';
|
||||
|
||||
// 波色分析
|
||||
html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">🎨 波色比例</h4>';
|
||||
html += '<div style="display:flex;gap:15px;">';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#fce4ec;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#e74c3c;">' + cw.red + '</div><div>红波 ' + cw.red_pct + '%</div></div>';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#e3f2fd;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#3498db;">' + cw.blue + '</div><div>蓝波 ' + cw.blue_pct + '%</div></div>';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#e8f5e9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#2ecc71;">' + cw.green + '</div><div>绿波 ' + cw.green_pct + '%</div></div>';
|
||||
html += '</div>';
|
||||
|
||||
// 生肖分析
|
||||
html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">⭐ 生肖排名</h4>';
|
||||
html += '<div style="display:flex;flex-wrap:wrap;gap:5px;">';
|
||||
for (var i = 0; i < Math.min(zo.list.length, 12); i++) {
|
||||
var z = zo.list[i];
|
||||
html += '<div style="text-align:center;background:#f5f5f5;padding:5px 10px;border-radius:4px;"><div style="font-size:14px;font-weight:bold;">' + z.animal + '</div><div style="font-size:10px;color:#666;">' + z.count + ' (' + z.percent + '%)</div></div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
// 奇偶分析
|
||||
html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">⚖️ 奇偶分析</h4>';
|
||||
html += '<div style="display:flex;gap:15px;">';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#e74c3c;">' + oe.odd + ' (' + oe.odd_pct + '%)</div><div>奇数</div></div>';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#3498db;">' + oe.even + ' (' + oe.even_pct + '%)</div><div>偶数</div></div>';
|
||||
html += '</div>';
|
||||
|
||||
// 大小分析
|
||||
html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">📊 大小分析</h4>';
|
||||
html += '<div style="display:flex;gap:15px;">';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#f39c12;">' + bs.big + ' (' + bs.big_pct + '%)</div><div>大数(25-49)</div></div>';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#2ecc71;">' + bs.small + ' (' + bs.small_pct + '%)</div><div>小数(1-24)</div></div>';
|
||||
html += '</div>';
|
||||
|
||||
// 和值
|
||||
html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">📈 和值统计</h4>';
|
||||
html += '<div style="display:flex;gap:15px;">';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;">' + sm.avg + '</div><div>平均</div></div>';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#e74c3c;">' + sm.max + '</div><div>最大</div></div>';
|
||||
html += '<div style="flex:1;text-align:center;padding:8px;background:#f9f9f9;border-radius:6px;"><div style="font-size:20px;font-weight:bold;color:#3498db;">' + sm.min + '</div><div>最小</div></div>';
|
||||
html += '</div>';
|
||||
|
||||
// 尾数
|
||||
html += '<h4 style="border-bottom:1px solid #eee;padding-bottom:5px;">🔢 尾数频率</h4>';
|
||||
html += '<div style="display:flex;flex-wrap:wrap;gap:5px;">';
|
||||
for (var i = 0; i < tn.all.length; i++) {
|
||||
var t = tn.all[i];
|
||||
html += '<div style="text-align:center;background:#f5f5f5;padding:5px 10px;border-radius:4px;"><div style="font-size:16px;font-weight:bold;">' + t.tail + '</div><div style="font-size:10px;color:#666;">' + t.count + ' (' + t.percent + '%)</div></div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
html += '</div>';
|
||||
$('#dash-result', layero).html(html);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user