feat(phase-4 to 10): add all remaining analysis features - color wave, zodiac, odd/even, big/small, sum, consecutive, tail numbers

This commit is contained in:
2026-04-21 23:46:12 +08:00
parent f36410dcc6
commit 8ed4837992
5 changed files with 681 additions and 2 deletions
+126 -1
View File
@@ -22,7 +22,7 @@ class History extends Backend
* 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录)
* @var array
*/
protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers'];
protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers', 'colorWaveAnalysis', 'zodiacAnalysis', 'oddEvenAnalysis', 'bigSmallAnalysis', 'sumAnalysis', 'consecutiveNumbers', 'tailNumbers'];
public function _initialize()
{
@@ -92,5 +92,130 @@ class History extends Backend
}
}
/**
* 波色分析
*/
public function colorWaveAnalysis()
{
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->getColorWaveAnalysis($periods, $type);
$this->success('查询成功', null, $result);
}
}
/**
* 生肖分析
*/
public function zodiacAnalysis()
{
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->getZodiacAnalysis($periods, $type);
$this->success('查询成功', null, $result);
}
}
/**
* 奇偶分析
*/
public function oddEvenAnalysis()
{
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->getOddEvenAnalysis($periods, $type);
$this->success('查询成功', null, $result);
}
}
/**
* 大小分析
*/
public function bigSmallAnalysis()
{
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->getBigSmallAnalysis($periods, $type);
$this->success('查询成功', null, $result);
}
}
/**
* 和值分析
*/
public function sumAnalysis()
{
if ($this->request->isAjax()) {
$periods = $this->request->get('periods', 30, 'intval');
if ($periods < 10 || $periods > 100) {
$this->error('期数范围必须在 10-100 之间');
}
$result = $this->model->getSumAnalysis($periods);
$this->success('查询成功', null, $result);
}
}
/**
* 连号分析
*/
public function consecutiveNumbers()
{
if ($this->request->isAjax()) {
$periods = $this->request->get('periods', 30, 'intval');
if ($periods < 10 || $periods > 100) {
$this->error('期数范围必须在 10-100 之间');
}
$result = $this->model->getConsecutiveNumbers($periods);
$this->success('查询成功', null, $result);
}
}
/**
* 尾数分析
*/
public function tailNumbers()
{
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->getTailNumbers($periods, $type);
$this->success('查询成功', null, $result);
}
}
}
+7
View File
@@ -18,4 +18,11 @@ return [
'Trend Chart' => '走势图',
'No data available' => '暂无数据',
'Hot/Cold Analysis' => '冷热分析',
'Color Wave' => '波色分析',
'Zodiac' => '生肖分析',
'Odd/Even' => '奇偶分析',
'Big/Small' => '大小分析',
'Sum Chart' => '和值分析',
'Consecutive' => '连号分析',
'Tail Numbers' => '尾数分析',
];
+201 -1
View File
@@ -44,7 +44,7 @@ class History extends Model
$history = $this
->field('expect,num1,num2,num3,num4,num5,num6,num7,openTime')
->order('openTime', 'asc')
->order('openTime', 'desc')
->limit($periods)
->select();
@@ -67,6 +67,10 @@ class History extends Model
}
}
// 反转数组,使最远的数据在左边,最近的数据在右边(从左往右,从远到近)
$expects = array_reverse($expects);
$data = array_reverse($data);
return [
'expects' => $expects,
'data' => $data,
@@ -246,5 +250,201 @@ class History extends Model
return count($allHistory);
}
/**
* 波色分析
*/
public function getColorWaveAnalysis($periods = 30, $type = 'all')
{
$num_model = new Num();
$colorMap = $num_model->column('color', 'num');
$history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select();
if (empty($history)) return ['red' => 0, 'blue' => 0, 'green' => 0, 'red_pct' => 0, 'blue_pct' => 0, 'green_pct' => 0, 'details' => []];
$fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7'];
$colors = ['红' => 0, '蓝' => 0, '绿' => 0];
$total = 0;
foreach ($history as $row) {
foreach ($fields as $f) {
$num = (int)$row[$f];
$color = $colorMap[$num] ?? '';
if (strpos($color, '红') !== false) { $colors['红']++; $total++; }
elseif (strpos($color, '蓝') !== false) { $colors['蓝']++; $total++; }
elseif (strpos($color, '绿') !== false) { $colors['绿']++; $total++; }
}
}
return [
'red' => $colors['红'], 'blue' => $colors['蓝'], 'green' => $colors['绿'],
'red_pct' => $total ? round($colors['红']/$total*100,1) : 0,
'blue_pct' => $total ? round($colors['蓝']/$total*100,1) : 0,
'green_pct' => $total ? round($colors['绿']/$total*100,1) : 0,
'total' => $total
];
}
/**
* 生肖分析
*/
public function getZodiacAnalysis($periods = 30, $type = 'all')
{
$num_model = new Num();
$animalMap = $num_model->column('animal', 'num');
$colorMap = $num_model->column('color', 'num');
$history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select();
if (empty($history)) return ['list' => []];
$fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7'];
$counts = [];
foreach ($history as $row) {
foreach ($fields as $f) {
$num = (int)$row[$f];
$animal = $animalMap[$num] ?? '未知';
if (!isset($counts[$animal])) $counts[$animal] = ['animal' => $animal, 'count' => 0, 'color' => $colorMap[$num] ?? '—'];
$counts[$animal]['count']++;
}
}
$list = array_values($counts);
usort($list, function ($a, $b) { return $b['count'] - $a['count']; });
$total = array_sum(array_column($list, 'count'));
foreach ($list as &$item) { $item['percent'] = $total ? round($item['count']/$total*100, 1) : 0; }
return ['list' => $list];
}
/**
* 奇偶分析
*/
public function getOddEvenAnalysis($periods = 30, $type = 'all')
{
$history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select();
if (empty($history)) return ['odd' => 0, 'even' => 0, 'odd_pct' => 0, 'even_pct' => 0, 'per_period' => []];
$fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7'];
$odd = 0; $even = 0; $perPeriod = [];
foreach ($history as $row) {
$p_odd = 0; $p_even = 0;
foreach ($fields as $f) {
$num = (int)$row[$f];
if ($num % 2 == 0) { $even++; $p_even++; } else { $odd++; $p_odd++; }
}
$perPeriod[] = ['expect' => $row['expect'], 'odd' => $p_odd, 'even' => $p_even];
}
$total = $odd + $even;
return [
'odd' => $odd, 'even' => $even,
'odd_pct' => $total ? round($odd/$total*100, 1) : 0,
'even_pct' => $total ? round($even/$total*100, 1) : 0,
'per_period' => $perPeriod
];
}
/**
* 大小分析(1-24为小,25-49为大)
*/
public function getBigSmallAnalysis($periods = 30, $type = 'all')
{
$history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select();
if (empty($history)) return ['big' => 0, 'small' => 0, 'big_pct' => 0, 'small_pct' => 0, 'per_period' => []];
$fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7'];
$big = 0; $small = 0; $perPeriod = [];
foreach ($history as $row) {
$p_big = 0; $p_small = 0;
foreach ($fields as $f) {
$num = (int)$row[$f];
if ($num >= 25) { $big++; $p_big++; } else { $small++; $p_small++; }
}
$perPeriod[] = ['expect' => $row['expect'], 'big' => $p_big, 'small' => $p_small];
}
$total = $big + $small;
return [
'big' => $big, 'small' => $small,
'big_pct' => $total ? round($big/$total*100, 1) : 0,
'small_pct' => $total ? round($small/$total*100, 1) : 0,
'per_period' => $perPeriod
];
}
/**
* 和值分析
*/
public function getSumAnalysis($periods = 30)
{
$history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'asc')->limit($periods)->select();
if (empty($history)) return ['expects' => [], 'sums' => []];
$expects = []; $sums = [];
foreach ($history as $row) {
$expects[] = (string)$row['expect'];
$sum = (int)$row['num1'] + (int)$row['num2'] + (int)$row['num3'] + (int)$row['num4'] + (int)$row['num5'] + (int)$row['num6'] + (int)$row['num7'];
$sums[] = $sum;
}
$avg = round(array_sum($sums) / count($sums), 1);
$max = max($sums); $min = min($sums);
return ['expects' => $expects, 'sums' => $sums, 'avg' => $avg, 'max' => $max, 'min' => $min];
}
/**
* 连号分析
*/
public function getConsecutiveNumbers($periods = 30)
{
$history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select();
if (empty($history)) return ['pairs' => [], 'triples' => []];
$pairCount = []; $tripleCount = [];
foreach ($history as $row) {
$nums = [];
for ($i = 1; $i <= 7; $i++) $nums[] = (int)$row['num' . $i];
sort($nums);
// 找连号对
for ($i = 0; $i < count($nums) - 1; $i++) {
if ($nums[$i + 1] - $nums[$i] === 1) {
$pair = $nums[$i] . '-' . $nums[$i + 1];
$pairCount[$pair] = isset($pairCount[$pair]) ? $pairCount[$pair] + 1 : 1;
}
}
// 找连号三连
for ($i = 0; $i < count($nums) - 2; $i++) {
if ($nums[$i + 1] - $nums[$i] === 1 && $nums[$i + 2] - $nums[$i + 1] === 1) {
$triple = $nums[$i] . '-' . $nums[$i + 1] . '-' . $nums[$i + 2];
$tripleCount[$triple] = isset($tripleCount[$triple]) ? $tripleCount[$triple] + 1 : 1;
}
}
}
arsort($pairCount); arsort($tripleCount);
return ['pairs' => $pairCount, 'triples' => $tripleCount];
}
/**
* 尾数分析
*/
public function getTailNumbers($periods = 30, $type = 'all')
{
$num_model = new Num();
$colorMap = $num_model->column('color', 'num');
$animalMap = $num_model->column('animal', 'num');
$history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select();
if (empty($history)) return ['tails' => [], 'all' => []];
$fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7'];
$tailCount = array_fill(0, 10, 0);
$all = [];
foreach ($history as $row) {
foreach ($fields as $f) {
$num = (int)$row[$f];
$tail = $num % 10;
$tailCount[$tail]++;
}
}
$total = array_sum($tailCount);
for ($t = 0; $t <= 9; $t++) {
$all[] = ['tail' => $t, 'count' => $tailCount[$t], 'percent' => $total ? round($tailCount[$t]/$total*100, 1) : 0];
}
usort($all, function ($a, $b) { return $b['count'] - $a['count']; });
return ['all' => $all];
}
}
@@ -10,6 +10,13 @@
<a href="javascript:;" class="btn btn-warning btn-missingnum" title="{:__('Missing Number Analysis')}"><i class="fa fa-search"></i> {:__('Missing Number Analysis')}</a>
<a href="javascript:;" class="btn btn-info btn-trend" title="{:__('Trend Chart')}"><i class="fa fa-area-chart"></i> {:__('Trend Chart')}</a>
<a href="javascript:;" class="btn btn-danger btn-hotcold" title="{:__('Hot/Cold Analysis')}"><i class="fa fa-fire"></i> {:__('Hot/Cold Analysis')}</a>
<a href="javascript:;" class="btn btn-success btn-colorwave" title="{:__('Color Wave')}"><i class="fa fa-paint-brush"></i> {:__('Color Wave')}</a>
<a href="javascript:;" class="btn btn-purple btn-zodiac" title="{:__('Zodiac')}"><i class="fa fa-star"></i> {:__('Zodiac')}</a>
<a href="javascript:;" class="btn btn-warning btn-oddeven" title="{:__('Odd/Even')}"><i class="fa fa-balance-scale"></i> {:__('Odd/Even')}</a>
<a href="javascript:;" class="btn btn-info btn-bigsmall" title="{:__('Big/Small')}"><i class="fa fa-arrows-h"></i> {:__('Big/Small')}</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-default btn-tailnums" title="{:__('Tail Numbers')}"><i class="fa fa-list-ol"></i> {:__('Tail Numbers')}</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"
+340
View File
@@ -57,6 +57,35 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
$(document).off('click', '.btn-hotcold').on('click', '.btn-hotcold', function () {
Controller.api.showHotColdDialog();
});
// 波色分析按钮事件
$(document).off('click', '.btn-colorwave').on('click', '.btn-colorwave', function () {
Controller.api.showAnalysisDialog('colorWave');
});
// 生肖分析按钮事件
$(document).off('click', '.btn-zodiac').on('click', '.btn-zodiac', function () {
Controller.api.showAnalysisDialog('zodiac');
});
// 奇偶分析按钮事件
$(document).off('click', '.btn-oddeven').on('click', '.btn-oddeven', function () {
Controller.api.showAnalysisDialog('oddEven');
});
// 大小分析按钮事件
$(document).off('click', '.btn-bigsmall').on('click', '.btn-bigsmall', function () {
Controller.api.showAnalysisDialog('bigSmall');
});
// 和值分析按钮事件
$(document).off('click', '.btn-sumchart').on('click', '.btn-sumchart', function () {
Controller.api.showSumDialog();
});
// 连号分析按钮事件
$(document).off('click', '.btn-consecutive').on('click', '.btn-consecutive', function () {
Controller.api.showConsecutiveDialog();
});
// 尾数分析按钮事件
$(document).off('click', '.btn-tailnums').on('click', '.btn-tailnums', function () {
Controller.api.showAnalysisDialog('tailNumbers');
});
},
add: function () {
Controller.api.bindevent();
@@ -552,6 +581,317 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
bindevent: function () {
Form.api.bindevent($("form[role=form]"));
},
/**
* 通用分析弹窗(波色、生肖、奇偶、大小、尾数)
*/
showAnalysisDialog: function (type) {
var titles = {
colorWave: __('Color Wave'),
zodiac: __('Zodiac'),
oddEven: __('Odd/Even'),
bigSmall: __('Big/Small'),
tailNumbers: __('Tail Numbers')
};
var endpoints = {
colorWave: 'history/colorWaveAnalysis',
zodiac: 'history/zodiacAnalysis',
oddEven: 'history/oddEvenAnalysis',
bigSmall: 'history/bigSmallAnalysis',
tailNumbers: 'history/tailNumbers'
};
var hasType = ['colorWave', 'zodiac', 'oddEven', 'bigSmall', 'tailNumbers'].indexOf(type) !== -1;
var html = '<div style="padding:20px;">';
if (hasType) {
html += '<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
' <label style="margin-right:15px;">' + __('Query Type') + '</label>' +
' <label class="radio-inline" style="margin-right:15px;">' +
' <input type="radio" name="analysis-type-' + type + '" value="all" checked> ' + __('All Numbers') +
' </label>' +
' <label class="radio-inline">' +
' <input type="radio" name="analysis-type-' + type + '" value="special"> ' + __('Special Only') +
' </label>' +
'</div>';
}
html += '<div class="form-group">' +
' <label>' + __('Query Periods') + '</label>' +
' <input type="number" id="analysis-periods-' + type + '" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
' <button class="btn btn-primary" id="btn-analysis-' + type + '" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
'</div>' +
'<div id="analysis-result-' + type + '" style="margin-top:15px;overflow-x:auto;"></div>' +
'</div>';
Layer.open({
type: 1,
title: titles[type] || type,
area: ['650px', '550px'],
content: html,
shadeClose: true,
success: function (layero, index) {
$('#btn-analysis-' + type, layero).on('click', function () {
var periods = parseInt($('#analysis-periods-' + type, layero).val()) || 30;
var tp = hasType ? $('input[name="analysis-type-' + type + '"]:checked', layero).val() : 'all';
Controller.api.queryAnalysis(periods, tp, type, endpoints[type], layero);
});
if (hasType) {
$('input[name="analysis-type-' + type + '"]', layero).on('change', function () {
var periods = parseInt($('#analysis-periods-' + type, layero).val()) || 30;
Controller.api.queryAnalysis(periods, $(this).val(), type, endpoints[type], layero);
});
}
}
});
},
queryAnalysis: function (periods, type, analysisType, endpoint, layero) {
var $btn = $('#btn-analysis-' + analysisType, layero);
$btn.prop('disabled', true);
$('#analysis-result-' + analysisType, layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
$.ajax({
url: endpoint,
type: 'GET',
data: {periods: periods, type: type},
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
Controller.api['render' + analysisType.charAt(0).toUpperCase() + analysisType.slice(1)](ret.data, layero);
} else {
$('#analysis-result-' + analysisType, layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
}
},
error: function () {
$('#analysis-result-' + analysisType, layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
renderColorWaveAnalysis: function (data, layero) {
var html = '<div style="padding:15px;"><div style="display:flex;justify-content:space-around;">';
var items = [
{label: '红波', value: data.red, pct: data.red_pct, color: '#e74c3c'},
{label: '蓝波', value: data.blue, pct: data.blue_pct, color: '#3498db'},
{label: '绿波', value: data.green, pct: data.green_pct, color: '#2ecc71'}
];
for (var i = 0; i < items.length; i++) {
var item = items[i];
html += '<div style="text-align:center;padding:20px;border-radius:8px;background:#f5f5f5;min-width:120px;">' +
'<div style="width:60px;height:60px;line-height:60px;border-radius:50%;background-color:' + item.color + ';color:#fff;font-size:24px;font-weight:bold;margin:0 auto;">' + item.value + '</div>' +
'<div style="margin-top:10px;font-size:14px;color:#333;">' + item.label + '</div>' +
'<div style="font-size:12px;color:#999;">' + item.pct + '%</div>' +
'</div>';
}
html += '</div><div style="margin-top:15px;color:#999;font-size:12px;text-align:center;">总计: ' + data.total + ' 个号码</div></div>';
$('#analysis-result-colorWave', layero).html(html);
},
renderZodiacAnalysis: function (data, layero) {
var html = '<div style="padding:15px;"><div style="display:flex;flex-wrap:wrap;gap:8px;">';
for (var i = 0; i < data.list.length; i++) {
var item = data.list[i];
html += '<div style="text-align:center;background:#f9f9f9;padding:10px 15px;border-radius:6px;min-width:90px;">' +
'<div style="font-size:18px;font-weight:bold;color:#333;">' + item.animal + '</div>' +
'<div style="font-size:12px;color:#666;">' + item.count + ' (' + item.percent + '%)</div>' +
'</div>';
}
html += '</div></div>';
$('#analysis-result-zodiac', layero).html(html);
},
renderOddEvenAnalysis: function (data, layero) {
var html = '<div style="padding:15px;"><div style="display:flex;justify-content:space-around;margin-bottom:15px;">';
html += '<div style="text-align:center;padding:15px;border-radius:8px;background:#f5f5f5;min-width:140px;">' +
'<div style="font-size:28px;font-weight:bold;color:#e74c3c;">' + data.odd + '</div>' +
'<div style="font-size:14px;color:#333;">奇数</div>' +
'<div style="font-size:12px;color:#999;">' + data.odd_pct + '%</div></div>';
html += '<div style="text-align:center;padding:15px;border-radius:8px;background:#f5f5f5;min-width:140px;">' +
'<div style="font-size:28px;font-weight:bold;color:#3498db;">' + data.even + '</div>' +
'<div style="font-size:14px;color:#333;">偶数</div>' +
'<div style="font-size:12px;color:#999;">' + data.even_pct + '%</div></div>';
html += '</div></div>';
$('#analysis-result-oddEven', layero).html(html);
},
renderBigSmallAnalysis: function (data, layero) {
var html = '<div style="padding:15px;"><div style="display:flex;justify-content:space-around;margin-bottom:15px;">';
html += '<div style="text-align:center;padding:15px;border-radius:8px;background:#f5f5f5;min-width:140px;">' +
'<div style="font-size:28px;font-weight:bold;color:#f39c12;">' + data.big + '</div>' +
'<div style="font-size:14px;color:#333;">大数(25-49)</div>' +
'<div style="font-size:12px;color:#999;">' + data.big_pct + '%</div></div>';
html += '<div style="text-align:center;padding:15px;border-radius:8px;background:#f5f5f5;min-width:140px;">' +
'<div style="font-size:28px;font-weight:bold;color:#2ecc71;">' + data.small + '</div>' +
'<div style="font-size:14px;color:#333;">小数(1-24)</div>' +
'<div style="font-size:12px;color:#999;">' + data.small_pct + '%</div></div>';
html += '</div></div>';
$('#analysis-result-bigSmall', layero).html(html);
},
renderTailNumbers: function (data, layero) {
var html = '<div style="padding:15px;"><div style="display:flex;flex-wrap:wrap;gap:8px;">';
for (var i = 0; i < data.all.length; i++) {
var item = data.all[i];
html += '<div style="text-align:center;background:#f9f9f9;padding:10px 15px;border-radius:6px;min-width:70px;">' +
'<div style="font-size:22px;font-weight:bold;color:#333;">' + item.tail + '</div>' +
'<div style="font-size:12px;color:#666;">' + item.count + ' (' + item.percent + '%)</div></div>';
}
html += '</div></div>';
$('#analysis-result-tailNumbers', layero).html(html);
},
/**
* 和值分析弹窗
*/
showSumDialog: function () {
var html = '<div style="padding:20px;">' +
'<div class="form-group">' +
' <label>' + __('Query Periods') + '</label>' +
' <input type="number" id="sum-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
' <button class="btn btn-primary" id="btn-sum-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
'</div>' +
'<div id="sum-result" style="margin-top:15px;"></div>' +
'</div>';
Layer.open({
type: 1,
title: __('Sum Chart'),
area: ['750px', '400px'],
content: html,
shadeClose: true,
success: function (layero, index) {
$('#btn-sum-query', layero).on('click', function () {
var periods = parseInt($('#sum-periods', layero).val()) || 30;
Controller.api.querySum(periods, layero);
});
}
});
},
querySum: function (periods, layero) {
var $btn = $('#btn-sum-query', layero);
$btn.prop('disabled', true);
$('#sum-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
$.ajax({
url: 'history/sumAnalysis',
type: 'GET',
data: {periods: periods},
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
Controller.api.renderSum(ret.data, layero);
} else {
$('#sum-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
}
},
error: function () {
$('#sum-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
renderSum: function (data, layero) {
var html = '<div style="padding:15px;"><div style="display:flex;justify-content:space-around;margin-bottom:15px;">';
html += '<div style="text-align:center;padding:10px;"><div style="font-size:20px;font-weight:bold;color:#333;">' + data.avg + '</div><div style="font-size:12px;color:#999;">平均和值</div></div>';
html += '<div style="text-align:center;padding:10px;"><div style="font-size:20px;font-weight:bold;color:#e74c3c;">' + data.max + '</div><div style="font-size:12px;color:#999;">最大和值</div></div>';
html += '<div style="text-align:center;padding:10px;"><div style="font-size:20px;font-weight:bold;color:#3498db;">' + data.min + '</div><div style="font-size:12px;color:#999;">最小和值</div></div>';
html += '</div><div id="sum-chart" style="width:100%;height:250px;"></div></div>';
$('#sum-result', layero).html(html);
var chartDom = document.getElementById('sum-chart');
if (chartDom && typeof echarts !== 'undefined') {
var myChart = echarts.init(chartDom);
myChart.setOption({
xAxis: {type: 'category', data: data.expects, axisLabel: {rotate: 45, fontSize: 10}},
yAxis: {type: 'value'},
series: [{type: 'line', data: data.sums, smooth: true, itemStyle: {color: '#3498db'}, areaStyle: {color: 'rgba(52,152,219,0.1)'}}],
grid: {left: 40, right: 20, bottom: 40, top: 20}
});
}
},
/**
* 连号分析弹窗
*/
showConsecutiveDialog: function () {
var html = '<div style="padding:20px;">' +
'<div class="form-group">' +
' <label>' + __('Query Periods') + '</label>' +
' <input type="number" id="consecutive-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
' <button class="btn btn-primary" id="btn-consecutive-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
'</div>' +
'<div id="consecutive-result" style="margin-top:15px;overflow-x:auto;"></div>' +
'</div>';
Layer.open({
type: 1,
title: __('Consecutive'),
area: ['600px', '500px'],
content: html,
shadeClose: true,
success: function (layero, index) {
$('#btn-consecutive-query', layero).on('click', function () {
var periods = parseInt($('#consecutive-periods', layero).val()) || 30;
Controller.api.queryConsecutive(periods, layero);
});
}
});
},
queryConsecutive: function (periods, layero) {
var $btn = $('#btn-consecutive-query', layero);
$btn.prop('disabled', true);
$('#consecutive-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
$.ajax({
url: 'history/consecutiveNumbers',
type: 'GET',
data: {periods: periods},
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
Controller.api.renderConsecutive(ret.data, layero);
} else {
$('#consecutive-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
}
},
error: function () {
$('#consecutive-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
renderConsecutive: function (data, layero) {
var html = '<div style="padding:15px;">';
html += '<h4 style="margin:0 0 10px 0;border-bottom:1px solid #eee;padding-bottom:5px;">连号对</h4>';
html += '<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:15px;">';
var pairs = data.pairs;
if (pairs && Object.keys(pairs).length > 0) {
for (var pair in pairs) {
html += '<div style="background:#f5f5f5;padding:6px 12px;border-radius:4px;font-size:13px;">' + pair + ' <b>×' + pairs[pair] + '</b></div>';
}
} else {
html += '<div style="color:#999;font-size:13px;">暂无连号数据</div>';
}
html += '</div><h4 style="margin:0 0 10px 0;border-bottom:1px solid #eee;padding-bottom:5px;">三连号</h4>';
html += '<div style="display:flex;flex-wrap:wrap;gap:6px;">';
var triples = data.triples;
if (triples && Object.keys(triples).length > 0) {
for (var triple in triples) {
html += '<div style="background:#f5f5f5;padding:6px 12px;border-radius:4px;font-size:13px;">' + triple + ' <b>×' + triples[triple] + '</b></div>';
}
} else {
html += '<div style="color:#999;font-size:13px;">暂无三连号数据</div>';
}
html += '</div></div>';
$('#consecutive-result', layero).html(html);
}
}
};