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},
]
]
});
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');
});
},
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 = '
' +
'
' + value + '';
if (animal) {
html += '
' + animal + '
';
}
html += '
';
return html;
}
},
/**
* 显示冷热分析弹窗
*/
showHotColdDialog: function () {
var html = '';
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(' ' + __('Loading') + '
');
$.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('' + (ret.msg || __('Query failed')) + '
');
}
},
error: function () {
$('#hc-result', layero).html('' + __('Query failed') + '
');
},
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 = '' + icon + ' ' + title + '
';
html += '
';
for (var i = 0; i < items.length; i++) {
var item = items[i];
var color = getColor(item.num);
var animal = getAnimal(item.num);
html += '
' +
'
' + item.num + '' +
'
' + (animal ? animal + '
' : '') + '' + item.count + ' (' + item.percent + '%)
' +
'
';
}
html += '
';
return html;
};
var html = '' +
renderSection('热号 Top 10', data.hot, '🔥') +
renderSection('冷号 Top 10', data.cold, '❄') +
'
';
$('#hc-result', layero).html(html);
},
/**
* 显示走势图弹窗
*/
showTrendDialog: function () {
var html = '';
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(' ' + __('Loading') + '
');
$.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('' + (ret.msg || __('Query failed')) + '
');
}
},
error: function () {
$('#trend-result', layero).html('' + __('Query failed') + '
');
},
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('' + __('No data available') + '
');
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('');
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 + '
';
for (var i = 0; i < params.length; i++) {
tip += params[i].seriesName + ': ' + params[i].data + '
';
}
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 = '';
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(' ' + __('Loading') + '
');
$.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('' + (ret.msg || __('Query failed')) + '
');
}
},
error: function () {
$('#missing-result', layero).html('' + __('Query failed') + '
');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
/**
* 渲染遗漏号码结果
*/
renderMissingNum: function (data, periods, layero) {
if (!data || data.length === 0) {
$('#missing-result', layero).html('' + __('No missing numbers found') + '
');
return;
}
var container = $('');
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 = $('');
var $ball = $('').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 = $('').append($ball);
if (animal) {
$content.append($('').text(animal));
}
$content.append($('').text(
__('Missing') + ' ' + data[i].omit + ' ' + __('periods')
));
$item.append($content);
container.append($item);
}
$('#missing-result', layero).html('').append(container);
},
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 = '';
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(' ' + __('Loading') + '
');
$.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('' + (ret.msg || __('Query failed')) + '
');
}
},
error: function () {
$('#analysis-result-' + analysisType, layero).html('' + __('Query failed') + '
');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
renderColorWaveAnalysis: function (data, layero) {
var html = '';
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 += '
' +
'
' + item.value + '
' +
'
' + item.label + '
' +
'
' + item.pct + '%
' +
'
';
}
html += '
总计: ' + data.total + ' 个号码
';
$('#analysis-result-colorWave', layero).html(html);
},
renderZodiacAnalysis: function (data, layero) {
var html = '';
for (var i = 0; i < data.list.length; i++) {
var item = data.list[i];
html += '
' +
'
' + item.animal + '
' +
'
' + item.count + ' (' + item.percent + '%)
' +
'
';
}
html += '
';
$('#analysis-result-zodiac', layero).html(html);
},
renderOddEvenAnalysis: function (data, layero) {
var html = '';
html += '
' +
'
' + data.odd + '
' +
'
奇数
' +
'
' + data.odd_pct + '%
';
html += '
' +
'
' + data.even + '
' +
'
偶数
' +
'
' + data.even_pct + '%
';
html += '
';
$('#analysis-result-oddEven', layero).html(html);
},
renderBigSmallAnalysis: function (data, layero) {
var html = '';
html += '
' +
'
' + data.big + '
' +
'
大数(25-49)
' +
'
' + data.big_pct + '%
';
html += '
' +
'
' + data.small + '
' +
'
小数(1-24)
' +
'
' + data.small_pct + '%
';
html += '
';
$('#analysis-result-bigSmall', layero).html(html);
},
renderTailNumbers: function (data, layero) {
var html = '';
for (var i = 0; i < data.all.length; i++) {
var item = data.all[i];
html += '
' +
'
' + item.tail + '
' +
'
' + item.count + ' (' + item.percent + '%)
';
}
html += '
';
$('#analysis-result-tailNumbers', layero).html(html);
},
/**
* 和值分析弹窗
*/
showSumDialog: function () {
var html = '' +
'
' +
' ' +
' ' +
' ' +
'
' +
'
' +
'
';
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(' ' + __('Loading') + '
');
$.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('' + (ret.msg || __('Query failed')) + '
');
}
},
error: function () {
$('#sum-result', layero).html('' + __('Query failed') + '
');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
renderSum: function (data, layero) {
var html = '';
html += '
';
html += '
';
html += '
';
html += '
';
$('#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 = '' +
'
' +
' ' +
' ' +
' ' +
'
' +
'
' +
'
';
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(' ' + __('Loading') + '
');
$.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('' + (ret.msg || __('Query failed')) + '
');
}
},
error: function () {
$('#consecutive-result', layero).html('' + __('Query failed') + '
');
},
complete: function () {
$btn.prop('disabled', false);
}
});
},
renderConsecutive: function (data, layero) {
var html = '';
html += '
连号对
';
html += '
';
var pairs = data.pairs;
if (pairs && Object.keys(pairs).length > 0) {
for (var pair in pairs) {
html += '
' + pair + ' ×' + pairs[pair] + '
';
}
} else {
html += '
暂无连号数据
';
}
html += '
三连号
';
html += '
';
var triples = data.triples;
if (triples && Object.keys(triples).length > 0) {
for (var triple in triples) {
html += '
' + triple + ' ×' + triples[triple] + '
';
}
} else {
html += '
暂无三连号数据
';
}
html += '
';
$('#consecutive-result', layero).html(html);
}
}
};
return Controller;
});