Files
amlhc/public/assets/js/backend/history.js
T
916117771 78e7233bc0 feat(history): 添加历史数据管理功能和数据分析图表
- 在addons.php中添加example模块路由配置
- 新增application/config.php配置文件,包含应用设置、数据库配置等
- 实现dashboard.js仪表盘功能,包含冷热号码分析、比例分析图表
- 添加history.js历史数据管理功能,支持号码查询和统计分析
- 集成echarts图表库实现数据可视化展示
- 添加号码颜色映射和生肖映射功能
- 实现号码球样式格式化显示
- 添加遗漏号码、走势图、冷热分析等数据分析功能
2026-04-25 22:35:24 +08:00

1205 lines
65 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefined, Backend, Table, Form) {
var Controller = {
index: function () {
Table.api.init({
extend: {
index_url: 'history/index' + location.search,
add_url: 'history/add',
edit_url: 'history/edit',
del_url: 'history/del',
multi_url: 'history/multi',
import_url: 'history/import',
table: 'history',
}
});
var table = $("#table");
// 从后端获取颜色和生肖映射
Controller.api.loadColorMap(function () {
Controller.api.loadAnimalMap(function () {
table.bootstrapTable({
url: $.fn.bootstrapTable.defaults.extend.index_url,
pk: 'expect',
sortName: 'expect',
columns: [
[
{checkbox: true},
{field: 'expect', title: __('Expect')},
{field: 'num1', title: __('Num1'), formatter: Controller.api.formatter.numBall},
{field: 'num2', title: __('Num2'), formatter: Controller.api.formatter.numBall},
{field: 'num3', title: __('Num3'), formatter: Controller.api.formatter.numBall},
{field: 'num4', title: __('Num4'), formatter: Controller.api.formatter.numBall},
{field: 'num5', title: __('Num5'), formatter: Controller.api.formatter.numBall},
{field: 'num6', title: __('Num6'), formatter: Controller.api.formatter.numBall},
{field: 'num7', title: __('Num7'), formatter: Controller.api.formatter.numBall},
{field: 'openTime', title: __('OpenTime'), operate:'RANGE', addclass:'datetimerange', autocomplete:false},
{field: 'search_day', title: '每月日号', visible: false, searchable: true, operate: '=', style: 'width:80px;', extend: 'min="1" max="31" placeholder="1-31"'}
]
]
});
Table.api.bindevent(table);
});
});
// 遗漏号码按钮事件
$(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();
});
// 冷热分析按钮事件
$(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');
});
// 特码冷热按钮事件
$(document).off('click', '.btn-specialhotcold').on('click', '.btn-specialhotcold', function () {
Controller.api.showSpecialHotColdDialog();
});
// 综合统计面板按钮事件
$(document).off('click', '.btn-dashboard').on('click', '.btn-dashboard', function () {
Controller.api.showDashboard();
});
},
add: function () {
Controller.api.bindevent();
},
edit: function () {
Controller.api.bindevent();
},
api: {
colorMap: {},
colorMapLoaded: false,
animalMap: {},
animalMapLoaded: false,
/**
* 从后端加载颜色映射并缓存
*/
loadColorMap: function (callback) {
if (Controller.api.colorMapLoaded) {
callback();
return;
}
$.ajax({
url: 'num/getColorMap',
type: 'GET',
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
Controller.api.colorMap = ret.msg;
}
Controller.api.colorMapLoaded = true;
callback();
}
});
},
/**
* 从后端加载生肖映射并缓存
*/
loadAnimalMap: function (callback) {
if (Controller.api.animalMapLoaded) {
callback();
return;
}
$.ajax({
url: 'num/getAnimalMap',
type: 'GET',
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
Controller.api.animalMap = ret.msg;
}
Controller.api.animalMapLoaded = true;
callback();
}
});
},
/**
* 根据数字从映射表中获取生肖
*/
getAnimalByNum: function (num) {
return Controller.api.animalMap[num] || '';
},
/**
* 根据数字从映射表中获取颜色
*/
getColorByNum: function (num) {
var color = Controller.api.colorMap[num];
if (!color) return '#95a5a6';
// 后端返回中文波色,前端映射为CSS颜色
if (color.indexOf('红') !== -1) return '#e74c3c';
if (color.indexOf('蓝') !== -1) return '#3498db';
if (color.indexOf('绿') !== -1) return '#2ecc71';
return '#95a5a6';
},
formatter: {
numBall: function (value, row, index) {
if (value === null || value === undefined || value === '') return '';
var num = parseInt(value);
var color = Controller.api.getColorByNum(num);
var animal = Controller.api.getAnimalByNum(num);
var html = '<div style="text-align:center;">' +
'<span class="num-ball" style="display:inline-block;width:32px;height:32px;line-height:32px;text-align:center;border-radius:50%;color:#fff;background-color:' + color + ';font-weight:bold;">' + value + '</span>';
if (animal) {
html += '<div style="font-size:10px;color:#666;line-height:1.2;">' + animal + '</div>';
}
html += '</div>';
return html;
}
},
/**
* 显示冷热分析弹窗
*/
showHotColdDialog: 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="hc-type" value="all" checked> ' + __('All Numbers') +
' </label>' +
' <label class="radio-inline">' +
' <input type="radio" name="hc-type" value="special"> ' + __('Special Only') +
' </label>' +
'</div>' +
'<div class="form-group">' +
' <label>' + __('Query Periods') + '</label>' +
' <input type="number" id="hc-periods" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
' <button class="btn btn-primary" id="btn-hc-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
'</div>' +
'<div id="hc-result" style="margin-top:15px;overflow-x:auto;"></div>' +
'</div>';
Layer.open({
type: 1,
title: __('Hot/Cold Analysis'),
area: ['700px', '600px'],
content: html,
shadeClose: true,
success: function (layero, index) {
$('#btn-hc-query', layero).on('click', function () {
var periods = parseInt($('#hc-periods', layero).val()) || 30;
var type = $('input[name="hc-type"]:checked', layero).val();
Controller.api.queryHotCold(periods, type, layero);
});
$('input[name="hc-type"]', layero).on('change', function () {
var periods = parseInt($('#hc-periods', layero).val()) || 30;
Controller.api.queryHotCold(periods, $(this).val(), layero);
});
}
});
},
queryHotCold: function (periods, type, layero) {
var $btn = $('#btn-hc-query', layero);
$btn.prop('disabled', true);
$('#hc-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
$.ajax({
url: 'history/hotColdNumbers',
type: 'GET',
data: {periods: periods, type: type},
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
Controller.api.renderHotCold(ret.data, layero);
} else {
$('#hc-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
}
},
error: function () {
$('#hc-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
renderHotCold: function (data, layero) {
var getColor = function (num) {
var color = data.all.find(function (item) { return item.num === num; });
if (!color || !color.color) return '#95a5a6';
if (color.color.indexOf('红') !== -1) return '#e74c3c';
if (color.color.indexOf('蓝') !== -1) return '#3498db';
if (color.color.indexOf('绿') !== -1) return '#2ecc71';
return '#95a5a6';
};
var getAnimal = function (num) {
var item = data.all.find(function (item) { return item.num === num; });
return item ? (item.animal || '') : '';
};
var renderSection = function (title, items, icon) {
var html = '<div style="margin-bottom:15px;"><h4 style="margin:0 0 8px 0;border-bottom:1px solid #eee;padding-bottom:5px;">' + icon + ' ' + title + '</h4>';
html += '<div style="display:flex;flex-wrap:wrap;gap:8px;">';
for (var i = 0; i < items.length; i++) {
var item = items[i];
var color = getColor(item.num);
var animal = getAnimal(item.num);
html += '<div style="text-align:center;background:#f9f9f9;padding:8px;border-radius:6px;min-width:70px;">' +
'<span style="display:inline-block;width:36px;height:36px;line-height:36px;text-align:center;border-radius:50%;color:#fff;background-color:' + color + ';font-weight:bold;">' + item.num + '</span>' +
'<div style="margin-top:4px;font-size:10px;color:#666;">' + (animal ? animal + '<br>' : '') + '<b>' + item.count + '</b> (' + item.percent + '%)</div>' +
'</div>';
}
html += '</div></div>';
return html;
};
var html = '<div style="padding:10px;">' +
renderSection('热号 Top 10', data.hot, '<span style="color:#e74c3c;">&#x1F525;</span>') +
renderSection('冷号 Top 10', data.cold, '<span style="color:#3498db;">&#x2744;</span>') +
'</div>';
$('#hc-result', layero).html(html);
},
/**
* 显示走势图弹窗
*/
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);
}
});
},
/**
* 渲染走势图(ECharts 折线图)
*/
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';
};
$('#trend-result', layero).html('<div id="trend-chart" style="width:100%;height:500px;"></div>');
var chartDom = document.getElementById('trend-chart');
var myChart = echarts.init(chartDom);
var series = [];
if (type === 'special') {
series = [{
name: '特码',
type: 'line',
data: rows.map(function (r) { return r.num7; }),
smooth: false,
symbol: 'circle',
symbolSize: 8,
lineStyle: { width: 2 },
itemStyle: {
color: function (params) {
var num = params.data;
return getColor(num);
}
},
label: {
show: true,
position: 'top',
fontSize: 11,
color: '#333'
}
}];
} else {
var numFields = [];
for (var c = 1; c <= 7; c++) {
numFields.push('num' + c);
}
var colors = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c', '#e67e22'];
for (var f = 0; f < numFields.length; f++) {
(function (idx) {
series.push({
name: '第' + (idx + 1) + '码',
type: 'line',
data: rows.map(function (r) { return r[numFields[idx]]; }),
smooth: false,
symbol: 'circle',
symbolSize: 6,
lineStyle: { width: 2 },
itemStyle: { color: colors[idx] },
label: {
show: true,
position: 'top',
fontSize: 10,
color: '#333'
}
});
})(f);
}
}
var option = {
title: { text: type === 'special' ? '特码走势' : '全部号码走势', left: 'center', textStyle: { fontSize: 14 } },
tooltip: { trigger: 'axis', formatter: function (params) {
var tip = '期号: ' + params[0].axisValueLabel + '<br/>';
for (var i = 0; i < params.length; i++) {
tip += params[i].seriesName + ': <b>' + params[i].data + '</b><br/>';
}
return tip;
}},
legend: { bottom: 10, data: series.map(function (s) { return s.name; }) },
grid: { left: 40, right: 20, bottom: 50, top: 40 },
xAxis: { type: 'category', data: expects, axisLabel: { rotate: 45, fontSize: 10 } },
yAxis: { type: 'value', min: 0, max: 50, splitLine: { show: true, lineStyle: { type: 'dashed' } } },
dataZoom: [{ type: 'slider', bottom: 30, height: 20, start: 0, end: 100 }],
series: series
};
myChart.setOption(option);
window.addEventListener('resize', function () { myChart.resize(); });
},
/**
* 显示遗漏号码分析弹窗
*/
showMissingNumDialog: 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="missing-type" value="all" checked> ' + __('All Numbers') +
' </label>' +
' <label class="radio-inline">' +
' <input type="radio" name="missing-type" value="special"> ' + __('Special Only') +
' </label>' +
'</div>' +
'<div class="form-group">' +
' <label>' + __('Query Periods') + '</label>' +
' <input type="number" id="missing-periods" class="form-control" value="10" min="1" max="100" style="width:120px;display:inline-block;">' +
' <button class="btn btn-primary" id="btn-missing-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
'</div>' +
'<div id="missing-result" style="margin-top:15px;"></div>' +
'</div>';
Layer.open({
type: 1,
title: __('Missing Number Analysis'),
area: ['650px', '550px'],
content: html,
shadeClose: true,
success: function (layero, index) {
// 绑定查询按钮事件
$('#btn-missing-query', layero).on('click', function () {
var periods = parseInt($('#missing-periods', layero).val()) || 10;
var type = $('input[name="missing-type"]:checked', layero).val();
Controller.api.queryMissingNum(periods, type, layero);
});
// 切换类型时自动查询
$('input[name="missing-type"]', layero).on('change', function () {
var periods = parseInt($('#missing-periods', layero).val()) || 10;
Controller.api.queryMissingNum(periods, $(this).val(), layero);
});
}
});
},
/**
* 查询遗漏号码
*/
queryMissingNum: function (periods, type, layero) {
// 确保颜色映射已加载
if (!Controller.api.colorMapLoaded) {
Controller.api.loadColorMap(function () {
Controller.api._doQueryMissingNum(periods, type, layero);
});
} else {
Controller.api._doQueryMissingNum(periods, type, layero);
}
},
/**
* 执行遗漏号码查询(内部方法)
*/
_doQueryMissingNum: function (periods, type, layero) {
var $btn = $('#btn-missing-query', layero);
$btn.prop('disabled', true);
$('#missing-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
$.ajax({
url: 'history/missingNum',
type: 'GET',
data: {periods: periods, type: type},
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
Controller.api.renderMissingNum(ret.data, periods, layero);
} else {
$('#missing-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
}
},
error: function () {
$('#missing-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
/**
* 渲染遗漏号码结果
*/
renderMissingNum: function (data, periods, layero) {
if (!data || data.length === 0) {
$('#missing-result', layero).html('<div class="alert alert-info">' + __('No missing numbers found') + '</div>');
return;
}
var container = $('<div style="display:flex;flex-wrap:wrap;gap:12px;"></div>');
for (var i = 0; i < data.length; i++) {
var color = Controller.api.getColorByNum(data[i].num);
var animal = Controller.api.getAnimalByNum(data[i].num);
var $item = $('<div style="text-align:center;"></div>');
var $ball = $('<span class="num-ball"></span>').css({
'display': 'inline-block',
'width': '48px',
'height': '48px',
'line-height': '48px',
'text-align': 'center',
'border-radius': '50%',
'color': '#fff',
'background-color': color,
'font-weight': 'bold',
'font-size': '18px'
}).text(data[i].num);
var $content = $('<div></div>').append($ball);
if (animal) {
$content.append($('<div style="margin-top:3px;font-size:11px;color:#666;line-height:1.2;"></div>').text(animal));
}
$content.append($('<div style="margin-top:3px;font-size:12px;color:#666;"></div>').text(
__('Missing') + ' ' + data[i].omit + ' ' + __('periods')
));
$item.append($content);
container.append($item);
}
$('#missing-result', layero).html('').append(container);
},
/**
* 特码冷热列表(每期相对于前N期的冷热状态)
*/
showSpecialHotColdDialog: function () {
var html = '<div style="padding:20px;">' +
'<div class="form-group" style="border-bottom:1px solid #eee;padding-bottom:10px;margin-bottom:10px;">' +
' <label>向前期数:</label>' +
' <input type="number" id="shc-lookback" class="form-control" value="30" min="10" max="100" style="width:120px;display:inline-block;">' +
' <button class="btn btn-primary" id="btn-shc-query" style="margin-left:10px;"><i class="fa fa-search"></i> ' + __('Query') + '</button>' +
'</div>' +
'<div id="shc-result" style="margin-top:10px;max-height:500px;overflow-y:auto;"></div>' +
'</div>';
Layer.open({
type: 1,
title: '特码冷热列表',
area: ['650px', '600px'],
content: html,
shadeClose: true,
success: function (layero, index) {
$('#btn-shc-query', layero).on('click', function () {
var lookback = parseInt($('#shc-lookback', layero).val()) || 30;
Controller.api.querySpecialHotCold(lookback, layero);
});
// 打开时自动查询
var lookback = parseInt($('#shc-lookback', layero).val()) || 30;
Controller.api.querySpecialHotCold(lookback, layero);
}
});
},
querySpecialHotCold: function (lookback, layero) {
var $btn = $('#btn-shc-query', layero);
$btn.prop('disabled', true);
$('#shc-result', layero).html('<div class="text-center"><i class="fa fa-spinner fa-spin"></i> ' + __('Loading') + '</div>');
$.ajax({
url: 'history/specialHotColdAction',
type: 'GET',
data: {lookback: lookback},
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
Controller.api.renderSpecialHotCold(ret.data, lookback, layero);
} else {
$('#shc-result', layero).html('<div class="alert alert-danger">' + (ret.msg || __('Query failed')) + '</div>');
}
},
error: function () {
$('#shc-result', layero).html('<div class="alert alert-danger">' + __('Query failed') + '</div>');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
renderSpecialHotCold: function (data, lookback, layero) {
var getColor = function (num) {
return Controller.api.getColorByNum(num);
};
var statusTag = function (status) {
var map = {
'hot': '<span style="color:#e74c3c;font-weight:bold;">&#x1F525; 热号</span>',
'cold': '<span style="color:#3498db;font-weight:bold;">&#x2744; 冷号</span>',
'normal': '<span style="color:#f39c12;font-weight:bold;">&#x279C; 温号</span>',
'unknown': '<span style="color:#999;">数据不足</span>'
};
return map[status] || '';
};
// 新接口返回 {list: [...], current: {hot: [], cold: [], warm: []}}
var listData = data.list || data;
var current = data.current || null;
if (!listData || listData.length === 0) {
$('#shc-result', layero).html('<div class="alert alert-info">暂无数据</div>');
return;
}
var html = '<div style="padding:0 5px;">';
// 当前冷热号汇总区域
if (current && (current.hot.length > 0 || current.cold.length > 0)) {
var renderNumBall = function (item) {
var color = getColor(item.num);
return '<span style="display:inline-block;width:30px;height:30px;line-height:30px;text-align:center;border-radius:50%;color:#fff;background-color:' + color + ';font-weight:bold;font-size:14px;" title="' + item.num + ' ×' + item.count + '">' + item.num + '</span>';
};
html += '<div style="background:#fafafa;border:1px solid #e0e0e0;border-radius:6px;padding:12px;margin-bottom:12px;">';
html += '<div style="font-size:13px;font-weight:bold;margin-bottom:8px;color:#333;"><i class="fa fa-bullseye"></i> 当前热号</div>';
html += '<div style="display:flex;flex-wrap:wrap;gap:6px;">';
for (var h = 0; h < current.hot.length; h++) {
html += renderNumBall(current.hot[h]);
}
if (current.hot.length === 0) {
html += '<span style="color:#999;font-size:12px;">暂无热号</span>';
}
html += '</div>';
html += '<div style="height:8px;"></div>';
html += '<div style="font-size:13px;font-weight:bold;margin-bottom:8px;color:#333;"><i class="fa fa-snowflake-o"></i> 当前冷号</div>';
html += '<div style="display:flex;flex-wrap:wrap;gap:6px;">';
for (var c = 0; c < current.cold.length; c++) {
html += renderNumBall(current.cold[c]);
}
if (current.cold.length === 0) {
html += '<span style="color:#999;font-size:12px;">暂无冷号</span>';
}
html += '</div>';
html += '</div>';
}
// 历史记录表格
html += '<table class="table table-striped table-bordered table-hover" style="font-size:13px;">' +
'<thead><tr>' +
'<th style="text-align:center;width:120px;">期号</th>' +
'<th style="text-align:center;width:60px;">特码</th>' +
'<th style="text-align:center;width:80px;">冷热</th>' +
'<th style="text-align:center;width:60px;">次数</th>' +
'<th style="text-align:center;width:60px;">平均</th>' +
'<th style="text-align:center;width:60px;">排名</th>' +
'</tr></thead><tbody>';
for (var i = 0; i < listData.length; i++) {
var item = listData[i];
var rowClass = '';
if (item.status === 'hot') rowClass = 'style="background:#fff5f5;"';
else if (item.status === 'cold') rowClass = 'style="background:#f5f8ff;"';
html += '<tr ' + rowClass + '>' +
'<td style="text-align:center;">' + item.expect + '</td>' +
'<td style="text-align:center;">' +
'<span style="display:inline-block;width:30px;height:30px;line-height:30px;text-align:center;border-radius:50%;color:#fff;background-color:' + getColor(item.specialNum) + ';font-weight:bold;font-size:14px;">' + item.specialNum + '</span>' +
'</td>' +
'<td style="text-align:center;">' + statusTag(item.status) + '</td>' +
'<td style="text-align:center;">' + item.count + '</td>' +
'<td style="text-align:center;">' + item.avgCount + '</td>' +
'<td style="text-align:center;">' + item.rank + '</td>' +
'</tr>';
}
html += '</tbody></table></div>';
$('#shc-result', layero).html(html);
},
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) {
var renderMap = {
colorWave: 'renderColorWaveAnalysis',
zodiac: 'renderZodiacAnalysis',
oddEven: 'renderOddEvenAnalysis',
bigSmall: 'renderBigSmallAnalysis',
tailNumbers: 'renderTailNumbers'
};
var renderFn = renderMap[analysisType];
if (renderFn && typeof Controller.api[renderFn] === 'function') {
Controller.api[renderFn](ret.data, layero);
} else {
$('#analysis-result-' + analysisType, layero).html('<div class="alert alert-danger">渲染方法不存在: ' + renderFn + '</div>');
}
} 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);
},
/**
* 综合统计面板
*/
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);
}
}
};
return Controller;
});