feat(phase-2): add trend chart analysis - grid view of number history

This commit is contained in:
2026-04-21 23:33:39 +08:00
parent f2b432a9f4
commit 616239392a
5 changed files with 203 additions and 1 deletions
+21 -1
View File
@@ -22,7 +22,7 @@ class History extends Backend
* 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录)
* @var array
*/
protected $noNeedRight = ['missingNum'];
protected $noNeedRight = ['missingNum', 'trendData'];
public function _initialize()
{
@@ -52,5 +52,25 @@ class History extends Backend
}
}
/**
* 获取走势图数据
* @return void
*/
public function trendData()
{
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->getTrendData($periods, $type);
$this->success('查询成功', null, $result);
}
}
}
+2
View File
@@ -15,4 +15,6 @@ return [
'Query Type' => '查询类型',
'All Numbers' => '全部号码',
'Special Only' => '仅特码',
'Trend Chart' => '走势图',
'No data available' => '暂无数据',
];
+50
View File
@@ -30,6 +30,56 @@ class History extends Model
];
/**
* 获取走势图数据
* @param int $periods 查询最近多少期
* @param string $type 查询类型 all=全部号码 special=仅特码
* @return array {expects: [], data: [[num1,...], ...], colorMap: []}
*/
public function getTrendData($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,openTime')
->order('openTime', 'asc')
->limit($periods)
->select();
if (empty($history)) {
return ['expects' => [], 'data' => [], 'colorMap' => []];
}
$expects = [];
$data = [];
foreach ($history as $row) {
$expects[] = (string)$row['expect'];
if ($type === 'special') {
$num = (int)$row['num7'];
$data[] = [
'num7' => $num,
'num7_color' => isset($colorMap[$num]) ? $colorMap[$num] : '—'
];
} else {
$row_data = [];
for ($i = 1; $i <= 7; $i++) {
$num = (int)$row['num' . $i];
$row_data['num' . $i] = $num;
$row_data['num' . $i . '_color'] = isset($colorMap[$num]) ? $colorMap[$num] : '—';
}
$data[] = $row_data;
}
}
return [
'expects' => $expects,
'data' => $data,
'colorMap' => $colorMap
];
}
/**
* 计算遗漏号码
* @param int $periods 查询最近多少期
@@ -8,6 +8,7 @@
<div id="toolbar" class="toolbar">
<a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a>
<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-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"
+129
View File
@@ -47,6 +47,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
$(document).off('click', '.btn-missingnum').on('click', '.btn-missingnum', function () {
Controller.api.showMissingNumDialog();
});
// 走势图按钮事件
$(document).off('click', '.btn-trend').on('click', '.btn-trend', function () {
Controller.api.showTrendDialog();
});
},
add: function () {
Controller.api.bindevent();
@@ -140,6 +145,130 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
}
},
/**
* 显示走势图弹窗
*/
showTrendDialog: function () {
var html = '<div style="padding:20px;">' +
'<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="trend-type" value="all" checked> ' + __('All Numbers') +
' </label>' +
' <label class="radio-inline">' +
' <input type="radio" name="trend-type" value="special"> ' + __('Special Only') +
' </label>' +
'</div>' +
'<div class="form-group">' +
' <label>' + __('Query Periods') + '</label>' +
' <input type="number" id="trend-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
' <button class="btn btn-primary" id="btn-trend-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
'</div>' +
'<div id="trend-result" style="margin-top:15px;overflow-x:auto;"></div>' +
'</div>';
Layer.open({
type: 1,
title: __('Trend Chart'),
area: ['90%', '80%'],
content: html,
shadeClose: false,
maxmin: true,
success: function (layero, index) {
$('#btn-trend-query', layero).on('click', function () {
var periods = parseInt($('#trend-periods', layero).val()) || 30;
var type = $('input[name="trend-type"]:checked', layero).val();
Controller.api.queryTrend(periods, type, layero);
});
$('input[name="trend-type"]', layero).on('change', function () {
var periods = parseInt($('#trend-periods', layero).val()) || 30;
Controller.api.queryTrend(periods, $(this).val(), layero);
});
}
});
},
/**
* 查询走势图
*/
queryTrend: function (periods, type, layero) {
var $btn = $('#btn-trend-query', layero);
$btn.prop('disabled', true);
$('#trend-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
$.ajax({
url: 'history/trendData',
type: 'GET',
data: {periods: periods, type: type},
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
Controller.api.renderTrend(ret.data, type, layero);
} else {
$('#trend-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
}
},
error: function () {
$('#trend-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
/**
* 渲染走势图(网格形式)
*/
renderTrend: function (data, type, layero) {
var expects = data.expects;
var rows = data.data;
var colorMap = data.colorMap;
if (!expects || expects.length === 0) {
$('#trend-result', layero).html('<div class="alert alert-info">' + __('No data available') + '</div>');
return;
}
var getColor = function (num) {
var color = colorMap[num];
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 html = '<table class="table table-bordered table-condensed" style="font-size:12px;white-space:nowrap;">';
html += '<thead><tr><th>期号</th>';
if (type === 'special') {
html += '<th style="text-align:center;">特码</th>';
} else {
for (var c = 1; c <= 7; c++) {
html += '<th style="text-align:center;">' + c + '</th>';
}
}
html += '</tr></thead><tbody>';
for (var i = 0; i < expects.length; i++) {
html += '<tr><td>' + expects[i] + '</td>';
if (type === 'special') {
var num = rows[i].num7;
var color = getColor(num);
html += '<td style="text-align:center;"><span style="display:inline-block;width:28px;height:28px;line-height:28px;text-align:center;border-radius:50%;color:#fff;background-color:' + color + ';font-weight:bold;">' + num + '</span></td>';
} else {
for (var j = 1; j <= 7; j++) {
var val = rows[i]['num' + j];
var c = getColor(val);
html += '<td style="text-align:center;"><span style="display:inline-block;width:28px;height:28px;line-height:28px;text-align:center;border-radius:50%;color:#fff;background-color:' + c + ';font-weight:bold;">' + val + '</span></td>';
}
}
html += '</tr>';
}
html += '</tbody></table>';
$('#trend-result', layero).html(html);
},
/**
* 显示遗漏号码分析弹窗
*/