Files
amlhc/public/assets/js/backend/history.js
T

560 lines
27 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},
]
]
});
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();
});
},
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);
},
bindevent: function () {
Form.api.bindevent($("form[role=form]"));
}
}
};
return Controller;
});