mt5-manager/static/script.js

201 lines
7 KiB
JavaScript
Raw Permalink Normal View History

2025-10-14 16:04:37 +03:00
/**
* @file script.js
* @version 0.1.0
*/
class TerminalCard {
constructor(name) {
this.name = name;
this.data = {status: 'stopped'};
this.$el = $(`[data-name="${name}"]`); // jQuery-объект элемента
this.$loadingContent = this.$el.find('.loading-content');
this.$loadedContent = this.$el.find('.loaded-content');
this.$btnStart = this.$el.find('.btn-start');
this.$btnStop = this.$el.find('.btn-stop');
this.$status = this.$el.find('.status')
}
2025-10-14 22:04:04 +03:00
onStartBefore() {
this.$btnStart
.prop('disabled', true)
.find('i').removeClass('fa-play').addClass('fa-spinner fa-spin');
}
2025-10-14 22:04:04 +03:00
onStartAfter() {
this.$btnStart.prop('disabled', true).find('i').removeClass('fa-spinner fa-spin').addClass('fa-play');
this.$btnStop.prop('disabled', false);
}
2025-10-10 10:16:59 +03:00
onStartCancel() {
this.$btnStart.prop('disabled', false);
}
2025-10-10 10:16:59 +03:00
onStopBefore() {
this.$btnStop.prop('disabled', true).find('i').removeClass('fa-pause').addClass('fa-spinner fa-spin');
}
2025-10-14 22:04:04 +03:00
onStopAfter() {
this.$btnStart.prop('disabled', false);
this.$btnStop.prop('disabled', true).find('i').removeClass('fa-spinner fa-spin').addClass('fa-pause');;
}
2025-10-14 22:04:04 +03:00
onStopCancel() {
this.$btnStop.prop('disabled', false);
}
2025-10-10 10:16:59 +03:00
updateData(data) {
// Обновляем содержимое карточки
if (data.account) {
let profitPct = (data.account.profit / data.account.balance)
// Обновляем данные в загруженном шаблоне
this.$el.find('.account').text(`...` + `${data.account.login}`.slice(-4))
.attr('title', `${data.account.server}: ${data.account.login}`);
2025-10-24 15:36:01 +03:00
this.$el.find('.balance').text(new Intl.NumberFormat('ru-RU', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: true
}).format(data.account.balance) + ' USD');
this.$el.find('.equity').text(new Intl.NumberFormat('ru-RU', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: true
}).format(data.account.equity));
this.$el.find('.profit').text(new Intl.NumberFormat('ru-RU', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: true,
signDisplay: 'always'
}).format(data.account.profit))
.removeClass('neg')
.addClass((a, b) => (data.account.profit < 0 ? 'neg' : ''));
this.$el.find('.profit-pct').text(
new Intl.NumberFormat('en-EN', {
style: 'percent',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
useGrouping: true,
signDisplay: 'always'
2025-10-24 15:36:01 +03:00
}).format(profitPct))
.removeClass('neg')
.addClass((a, b) => (data.account.profit < 0 ? 'neg' : ''));
this.$el.find('.company').addClass(`company-${data.account.server}`)
.attr('title', data.account.company);
} else {
// Обновляем данные в загруженном шаблоне
this.$loadedContent.find('.account').text(data.login);
this.$loadedContent.find('.server').text(data.server);
}
if (data.status == 'running') {
this.$btnStart.prop('disabled', true);
this.$btnStop.prop('disabled', false);
} else {
this.$btnStart.prop('disabled', false);
this.$btnStop.prop('disabled', true);
}
const icon = this.$status.find('i');
const desc = this.$status.find('.description');
icon.removeClass('fa-circle-check fa-stop fa-circle-exclamation fa-spin');
2025-10-14 14:16:34 +03:00
if (data.last_error.code == 1) {
this.$status.removeClass('text-danger text-secondary').addClass('text-success');
icon.addClass('fa-circle-check')
} else if (data.last_error.code == 0) {
this.$status.removeClass('text-danger text-success').addClass('text-secondary');
icon.addClass('fa-stop')
} else {
this.$status.removeClass('text-success text-secondary').addClass('text-danger');
icon.addClass('fa-circle-exclamation')
}
desc.text(data.last_error.description);
if (data.status != this.data.status) {
this.onStartAfter();
this.onStopAfter();
}
this.data = data
}
showLoadedState() {
this.$el.removeClass('loading-card').addClass('loaded-card');
}
}
const cards = {};
let updateInterval;
2025-10-21 21:24:09 +03:00
$(document).ready(function () {
// Загрузка данных при открытии страницы
2025-10-21 21:24:09 +03:00
terminals.forEach(function (name) {
cards[name] = new TerminalCard(name);
fetchTerminalInfo(name);
});
// Запуск автоматического обновления каждые 10 секунд
2025-10-21 21:24:09 +03:00
updateInterval = setInterval(function () {
terminals.forEach(function (name) {
fetchTerminalInfo(name);
});
2025-10-21 21:24:09 +03:00
}, 10000);
});
2025-10-21 21:24:09 +03:00
async function fetchTerminalInfo(name) {
$.post(`/instances/${name}`, function (data) {
if (data.error) {
console.error("Ошибка:", data.error);
return;
}
const card = cards[name];
card.updateData(data);
card.showLoadedState();
})
2025-10-21 21:24:09 +03:00
.fail(function (xhr, status, error) {
console.error("Ошибка при получении данных:", error);
// Опционально: показать ошибку
});
}
function startTerminal(name) {
cards[name].onStartBefore();
// Здесь вызов POST /start/{name}
$.post(`/start/${name}`, function (data) {
// toastr.success(`Terminal ${name} started`);
// Обновляем статус кнопки
fetchTerminalInfo(name);
})
.fail(function () {
toastr.error(`Не удалось запустить терминал ${name}`);
cards[name].onStartCancel();
});
}
function stopTerminal(name) {
cards[name].onStopBefore();
$.post(`/stop/${name}`, function (data) {
//toastr.success(`Terminal ${name} stopped`);
// Обновляем статус кнопки
fetchTerminalInfo(name);
cards[name].onStopAfter();
})
.fail(function () {
toastr.error(`Не удалось запустить терминал ${name}`);
cards[name].onStopCancel();
});
}
// Остановить обновления при закрытии вкладки (опционально)
2025-10-21 21:24:09 +03:00
$(window).on("beforeunload", function () {
clearInterval(updateInterval);
2025-10-21 21:24:09 +03:00
});