78e7233bc0
- 在addons.php中添加example模块路由配置 - 新增application/config.php配置文件,包含应用设置、数据库配置等 - 实现dashboard.js仪表盘功能,包含冷热号码分析、比例分析图表 - 添加history.js历史数据管理功能,支持号码查询和统计分析 - 集成echarts图表库实现数据可视化展示 - 添加号码颜色映射和生肖映射功能 - 实现号码球样式格式化显示 - 添加遗漏号码、走势图、冷热分析等数据分析功能
294 lines
16 KiB
JavaScript
294 lines
16 KiB
JavaScript
define(['jquery'], function ($) {
|
|
var Controller = {
|
|
index: function () {
|
|
var getColor = function (color) {
|
|
if (!color || color === '—') return '#95a5a6';
|
|
if (color.indexOf('红') !== -1) return '#e74c3c';
|
|
if (color.indexOf('蓝') !== -1) return '#3498db';
|
|
if (color.indexOf('绿') !== -1) return '#2ecc71';
|
|
return '#95a5a6';
|
|
};
|
|
|
|
var ball = function (num, color) {
|
|
return '<span class="num-ball-sm" style="background-color:' + getColor(color) + ';">' + num + '</span>';
|
|
};
|
|
|
|
function loadData() {
|
|
var periods = parseInt($('#dash-periods').val()) || 30;
|
|
if (periods < 10) periods = 10;
|
|
if (periods > 100) periods = 100;
|
|
$('#dash-content').html('<div class="text-center" style="padding:60px;"><i class="fa fa-spinner fa-spin fa-2x"></i><br><span class="text-muted">' + __('Loading') + '</span></div>');
|
|
|
|
$.ajax({
|
|
url: 'history/dashboard',
|
|
type: 'GET',
|
|
data: {periods: periods},
|
|
dataType: 'json',
|
|
success: function (ret) {
|
|
if (ret.code != 1) {
|
|
$('#dash-content').html('<div class="alert alert-danger">' + (ret.msg || '加载失败') + '</div>');
|
|
return;
|
|
}
|
|
render(ret.data);
|
|
},
|
|
error: function () {
|
|
$('#dash-content').html('<div class="alert alert-danger">请求失败,请重试</div>');
|
|
}
|
|
});
|
|
}
|
|
|
|
function render(data) {
|
|
var hc = data.hotcold, cw = data.colorwave, zo = data.zodiac;
|
|
var oe = data.oddeven, bs = data.bigsmall, sp = data.special, tn = data.tailnumbers;
|
|
|
|
var html = '';
|
|
|
|
// 热号:按次数降序取前5名
|
|
var hotAll = hc.all.slice().sort(function (a, b) { return b.count - a.count; });
|
|
var hotTop5 = hotAll.slice(0, 5);
|
|
// 冷号:按次数升序取前5名,第5名有并列则全部包含
|
|
var coldAll = hc.all.slice().sort(function (a, b) { return a.count - b.count; });
|
|
var coldList = coldAll.slice(0, 5);
|
|
if (coldList.length >= 5) {
|
|
var threshold = coldList[4].count;
|
|
var rest = coldAll.slice(5);
|
|
for (var k = 0; k < rest.length; k++) {
|
|
if (rest[k].count === threshold) coldList.push(rest[k]);
|
|
else break;
|
|
}
|
|
}
|
|
|
|
html += '<div class="dash-section"><h4>🔥❄️ 冷热号码</h4><div class="row">';
|
|
html += '<div class="col-sm-6"><div class="dash-card" style="border-left:3px solid #e74c3c;"><div style="color:#e74c3c;font-weight:bold;margin-bottom:8px;">热号 Top 5</div>';
|
|
html += '<div style="display:flex;flex-wrap:wrap;">';
|
|
for (var i = 0; i < hotTop5.length; i++) {
|
|
html += '<span style="flex:0 0 20%;text-align:center;margin-bottom:4px;">' + ball(hotTop5[i].num, hotTop5[i].color) + '<br><span style="font-size:11px;color:#999;">' + hotTop5[i].count + '次</span></span>';
|
|
}
|
|
html += '</div></div></div>';
|
|
html += '<div class="col-sm-6"><div class="dash-card" style="border-left:3px solid #3498db;"><div style="color:#3498db;font-weight:bold;margin-bottom:8px;">冷号</div>';
|
|
html += '<div style="display:flex;flex-wrap:wrap;">';
|
|
for (var i = 0; i < coldList.length; i++) {
|
|
html += '<span style="flex:0 0 20%;text-align:center;margin-bottom:4px;">' + ball(coldList[i].num, coldList[i].color) + '<br><span style="font-size:11px;color:#999;">' + coldList[i].count + '次</span></span>';
|
|
}
|
|
html += '</div></div></div></div></div>';
|
|
|
|
html += '<div class="dash-section"><h4>📊 比例分析</h4><div class="row">';
|
|
html += '<div class="col-sm-4"><div class="dash-card"><div id="colorwave-chart" style="width:100%;height:200px;"></div></div></div>';
|
|
html += '<div class="col-sm-4"><div class="dash-card"><div id="oddeven-chart" style="width:100%;height:200px;"></div></div></div>';
|
|
html += '<div class="col-sm-4"><div class="dash-card"><div id="bigsmall-chart" style="width:100%;height:200px;"></div></div></div>';
|
|
html += '</div></div>';
|
|
|
|
html += '<div class="dash-section"><h4>⭐ 生肖排名</h4><div id="zodiac-chart" style="width:100%;height:250px;"></div></div>';
|
|
|
|
html += '<div class="dash-section"><h4>📈 特码走势</h4><div id="special-chart" style="width:100%;height:280px;"></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);
|
|
|
|
// 波色饼图
|
|
if (typeof echarts !== 'undefined') {
|
|
var cwDom = document.getElementById('colorwave-chart');
|
|
if (cwDom) {
|
|
var cwChart = echarts.init(cwDom);
|
|
cwChart.setOption({
|
|
tooltip: {trigger: 'item', formatter: '{b}: {c} ({d}%)'},
|
|
series: [{
|
|
type: 'pie', radius: '60%',
|
|
data: [
|
|
{value: cw.red, name: '红波', itemStyle: {color: '#e74c3c'}},
|
|
{value: cw.blue, name: '蓝波', itemStyle: {color: '#3498db'}},
|
|
{value: cw.green, name: '绿波', itemStyle: {color: '#2ecc71'}}
|
|
],
|
|
label: {show: true, formatter: '{b}\n{d}%'},
|
|
emphasis: {itemStyle: {shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.5)'}}
|
|
}],
|
|
grid: {top: 0, bottom: 0}
|
|
});
|
|
$(window).on('resize', function(){ cwChart.resize(); });
|
|
}
|
|
|
|
// 奇偶饼图
|
|
var oeDom = document.getElementById('oddeven-chart');
|
|
if (oeDom) {
|
|
var oeChart = echarts.init(oeDom);
|
|
oeChart.setOption({
|
|
tooltip: {trigger: 'item', formatter: '{b}: {c} ({d}%)'},
|
|
series: [{
|
|
type: 'pie', radius: '60%',
|
|
data: [
|
|
{value: oe.odd, name: '奇数', itemStyle: {color: '#e74c3c'}},
|
|
{value: oe.even, name: '偶数', itemStyle: {color: '#3498db'}}
|
|
],
|
|
label: {show: true, formatter: '{b}\n{d}%'},
|
|
emphasis: {itemStyle: {shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.5)'}}
|
|
}],
|
|
grid: {top: 0, bottom: 0}
|
|
});
|
|
$(window).on('resize', function(){ oeChart.resize(); });
|
|
}
|
|
|
|
// 大小饼图
|
|
var bsDom = document.getElementById('bigsmall-chart');
|
|
if (bsDom) {
|
|
var bsChart = echarts.init(bsDom);
|
|
bsChart.setOption({
|
|
tooltip: {trigger: 'item', formatter: '{b}: {c} ({d}%)'},
|
|
series: [{
|
|
type: 'pie', radius: '60%',
|
|
data: [
|
|
{value: bs.big, name: '大数', itemStyle: {color: '#f39c12'}},
|
|
{value: bs.small, name: '小数', itemStyle: {color: '#2ecc71'}}
|
|
],
|
|
label: {show: true, formatter: '{b}\n{d}%'},
|
|
emphasis: {itemStyle: {shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.5)'}}
|
|
}],
|
|
grid: {top: 0, bottom: 0}
|
|
});
|
|
$(window).on('resize', function(){ bsChart.resize(); });
|
|
}
|
|
}
|
|
|
|
// 生肖柱状图
|
|
if (typeof echarts !== 'undefined' && zo.list && zo.list.length > 0) {
|
|
var zDom = document.getElementById('zodiac-chart');
|
|
if (zDom) {
|
|
var zChart = echarts.init(zDom);
|
|
zChart.setOption({
|
|
tooltip: {trigger: 'axis', axisPointer: {type: 'shadow'}},
|
|
grid: {left: 50, right: 20, bottom: 30, top: 10},
|
|
xAxis: {type: 'category', data: zo.list.map(function(z){return z.animal;})},
|
|
yAxis: {type: 'value', splitNumber: 3},
|
|
series: [{type: 'bar', data: zo.list.map(function(z){return z.count;}), itemStyle: {color: '#626c91'}, label: {show: true, position: 'top', fontSize: 12, color: '#333'}}]
|
|
});
|
|
$(window).on('resize', function(){ zChart.resize(); });
|
|
}
|
|
}
|
|
|
|
// 特码走势
|
|
if (typeof echarts !== 'undefined' && sp.expects && sp.expects.length > 0) {
|
|
var chartDom = document.getElementById('special-chart');
|
|
if (chartDom) {
|
|
var chart = echarts.init(chartDom);
|
|
var colorMap = {'红': '#e74c3c', '蓝': '#3498db', '绿': '#2ecc71'};
|
|
var itemColors = sp.colors.map(function(c) {
|
|
if (c.indexOf('红') !== -1) return colorMap['红'];
|
|
if (c.indexOf('蓝') !== -1) return colorMap['蓝'];
|
|
if (c.indexOf('绿') !== -1) return colorMap['绿'];
|
|
return '#95a5a6';
|
|
});
|
|
chart.setOption({
|
|
tooltip: {trigger: 'axis', formatter: function(p) {
|
|
return '期号: ' + p[0].axisValue + '<br/>特码: <b style="color:' + itemColors[p[0].dataIndex] + '">' + p[0].data + '</b>';
|
|
}},
|
|
xAxis: {type: 'category', data: sp.expects, axisLabel: {rotate: 45, fontSize: 10}},
|
|
yAxis: {type: 'value', min: 0, max: 50},
|
|
series: [{type: 'line', data: sp.specials, smooth: true, symbolSize: 8, itemStyle: {color: function(p) { return itemColors[p.dataIndex]; }}, areaStyle: {color: 'rgba(52,152,219,0.05)'}}],
|
|
grid: {left: 50, right: 20, bottom: 50, top: 20}
|
|
});
|
|
$(window).on('resize', function () { chart.resize(); });
|
|
}
|
|
}
|
|
|
|
// 尾数柱状图
|
|
if (typeof echarts !== 'undefined' && tn.all && tn.all.length > 0) {
|
|
var tDom = document.getElementById('tail-chart');
|
|
if (tDom) {
|
|
var tChart = echarts.init(tDom);
|
|
var sorted = tn.all.slice().sort(function(a,b){return a.tail - b.tail;});
|
|
tChart.setOption({
|
|
tooltip: {trigger: 'axis', axisPointer: {type: 'shadow'}},
|
|
grid: {left: 50, right: 20, bottom: 30, top: 10},
|
|
xAxis: {type: 'category', data: sorted.map(function(t){return t.tail;})},
|
|
yAxis: {type: 'value', splitNumber: 3},
|
|
series: [{type: 'bar', data: sorted.map(function(t){return t.count;}), itemStyle: {color: '#23b7e5'}, label: {show: true, position: 'top', fontSize: 12, color: '#333'}}]
|
|
});
|
|
$(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);
|
|
loadData();
|
|
}
|
|
};
|
|
|
|
return Controller;
|
|
});
|