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 登录)
|
* 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录)
|
||||||
* @var array
|
* @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()
|
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' => '和值分析',
|
'Sum Chart' => '和值分析',
|
||||||
'Consecutive' => '连号分析',
|
'Consecutive' => '连号分析',
|
||||||
'Tail Numbers' => '尾数分析',
|
'Tail Numbers' => '尾数分析',
|
||||||
|
'Dashboard' => '综合统计面板',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -446,5 +446,21 @@ class History extends Model
|
|||||||
return ['all' => $all];
|
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-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-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-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>-->
|
<!-- <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>
|
</div>
|
||||||
<table id="table" class="table table-striped table-bordered table-hover table-nowrap"
|
<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 () {
|
$(document).off('click', '.btn-tailnums').on('click', '.btn-tailnums', function () {
|
||||||
Controller.api.showAnalysisDialog('tailNumbers');
|
Controller.api.showAnalysisDialog('tailNumbers');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 综合统计面板按钮事件
|
||||||
|
$(document).off('click', '.btn-dashboard').on('click', '.btn-dashboard', function () {
|
||||||
|
Controller.api.showDashboard();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
add: function () {
|
add: function () {
|
||||||
Controller.api.bindevent();
|
Controller.api.bindevent();
|
||||||
@@ -904,6 +909,143 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
|
|||||||
}
|
}
|
||||||
html += '</div></div>';
|
html += '</div></div>';
|
||||||
$('#consecutive-result', layero).html(html);
|
$('#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