mirror of
https://github.com/cuigh/swirl
synced 2025-05-08 05:44:36 +00:00
Use ECharts instead of Chart.js
This commit is contained in:
parent
d5b5ff114a
commit
8d2575d229
File diff suppressed because it is too large
Load Diff
10
assets/chart/chart.bundle.min.js
vendored
10
assets/chart/chart.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
1
assets/echarts/echarts.min.js
vendored
Normal file
1
assets/echarts/echarts.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1120,22 +1120,61 @@ var Swirl;
|
||||
(function (Swirl) {
|
||||
var Core;
|
||||
(function (Core) {
|
||||
class GraphOptions {
|
||||
class ChartOptions {
|
||||
constructor() {
|
||||
this.type = "line";
|
||||
this.width = 12;
|
||||
this.height = 150;
|
||||
this.height = 200;
|
||||
}
|
||||
}
|
||||
Core.GraphOptions = GraphOptions;
|
||||
class Graph {
|
||||
constructor(elem, opts) {
|
||||
this.$elem = $(elem);
|
||||
this.opts = $.extend(new GraphOptions(), opts);
|
||||
this.name = this.$elem.data("chart-name");
|
||||
Core.ChartOptions = ChartOptions;
|
||||
class Chart {
|
||||
constructor(opts) {
|
||||
this.opts = $.extend(new ChartOptions(), opts);
|
||||
this.createElem();
|
||||
}
|
||||
getName() {
|
||||
return this.name;
|
||||
createElem() {
|
||||
this.$elem = $(`<div class="column is-${this.opts.width}" data-name="${this.opts.name}">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">${this.opts.title}</p>
|
||||
<a data-action="remove-chart" class="card-header-icon" aria-label="remove chart">
|
||||
<span class="icon">
|
||||
<i class="fas fa-times has-text-danger" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
</header>
|
||||
<div class="card-content" style="height: ${this.opts.height}px"></div>
|
||||
</div>
|
||||
</div>`);
|
||||
}
|
||||
init() {
|
||||
let opt = {
|
||||
legend: {
|
||||
x: 'right',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
animation: false
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
splitLine: { show: false },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%'],
|
||||
splitLine: { show: false },
|
||||
axisLabel: {
|
||||
formatter: this.formatValue.bind(this),
|
||||
},
|
||||
},
|
||||
};
|
||||
this.config(opt);
|
||||
this.chart = echarts.init(this.$elem.find("div.card-content").get(0));
|
||||
this.chart.setOption(opt, true);
|
||||
}
|
||||
getElem() {
|
||||
return this.$elem;
|
||||
@ -1143,54 +1182,13 @@ var Swirl;
|
||||
getOptions() {
|
||||
return this.opts;
|
||||
}
|
||||
}
|
||||
Core.Graph = Graph;
|
||||
class ValueGraph extends Graph {
|
||||
constructor(elem, opts) {
|
||||
super(elem, opts);
|
||||
}
|
||||
setData(d) {
|
||||
}
|
||||
resize(w, h) {
|
||||
}
|
||||
}
|
||||
Core.ValueGraph = ValueGraph;
|
||||
class ComplexGraph extends Graph {
|
||||
constructor(elem, opts) {
|
||||
super(elem, opts);
|
||||
if (!this.opts.colors) {
|
||||
this.opts.colors = ComplexGraph.defaultColors;
|
||||
}
|
||||
this.config = {
|
||||
type: opts.type,
|
||||
data: {},
|
||||
options: {
|
||||
responsive: false,
|
||||
maintainAspectRatio: false,
|
||||
animation: {
|
||||
duration: 0,
|
||||
},
|
||||
}
|
||||
};
|
||||
this.fillConfig();
|
||||
this.ctx = $(elem).find("canvas").get(0).getContext('2d');
|
||||
if (opts.height) {
|
||||
this.ctx.canvas.width = this.ctx.canvas.parentElement.offsetWidth;
|
||||
this.ctx.canvas.height = this.ctx.canvas.parentElement.offsetHeight;
|
||||
}
|
||||
this.chart = new Chart(this.ctx, this.config);
|
||||
}
|
||||
setData(d) {
|
||||
}
|
||||
resize(w, h) {
|
||||
this.ctx.canvas.width = this.ctx.canvas.parentElement.offsetWidth;
|
||||
this.ctx.canvas.height = this.ctx.canvas.parentElement.offsetHeight;
|
||||
resize() {
|
||||
this.chart.resize();
|
||||
}
|
||||
fillConfig() {
|
||||
config(opt) {
|
||||
}
|
||||
static formatValue(value, unit) {
|
||||
switch (unit) {
|
||||
formatValue(value) {
|
||||
switch (this.opts.unit) {
|
||||
case "percent:100":
|
||||
return value.toFixed(1) + "%";
|
||||
case "percent:1":
|
||||
@ -1220,136 +1218,166 @@ var Swirl;
|
||||
}
|
||||
}
|
||||
}
|
||||
ComplexGraph.defaultColors = [
|
||||
'rgb(255, 99, 132)',
|
||||
'rgb(75, 192, 192)',
|
||||
'rgb(255, 159, 64)',
|
||||
'rgb(54, 162, 235)',
|
||||
'rgb(153, 102, 255)',
|
||||
'rgb(255, 205, 86)',
|
||||
'rgb(201, 203, 207)',
|
||||
];
|
||||
Core.ComplexGraph = ComplexGraph;
|
||||
class VectorGraph extends ComplexGraph {
|
||||
fillConfig() {
|
||||
this.config.options.legend = {
|
||||
position: "right"
|
||||
};
|
||||
this.config.options.tooltips = {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
let label = data.labels[tooltipItem.index] + ": ";
|
||||
let p = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
if (typeof p == "number") {
|
||||
label += ComplexGraph.formatValue(p, this.opts.unit);
|
||||
}
|
||||
return label;
|
||||
}
|
||||
Core.Chart = Chart;
|
||||
class GaugeChart extends Chart {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
}
|
||||
config(opt) {
|
||||
$.extend(true, opt, {
|
||||
grid: {
|
||||
left: 0,
|
||||
top: 20,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
setData(d) {
|
||||
this.config.data.datasets = [{
|
||||
data: d.data,
|
||||
backgroundColor: this.opts.colors,
|
||||
}];
|
||||
this.config.data.labels = d.labels;
|
||||
this.chart.update();
|
||||
}
|
||||
}
|
||||
Core.VectorGraph = VectorGraph;
|
||||
class MatrixGraph extends ComplexGraph {
|
||||
constructor(elem, opts) {
|
||||
super(elem, opts);
|
||||
}
|
||||
fillConfig() {
|
||||
this.config.options.scales = {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'minute',
|
||||
tooltipFormat: 'YYYY/MM/DD HH:mm:ss',
|
||||
displayFormats: {
|
||||
minute: 'HH:mm'
|
||||
}
|
||||
},
|
||||
}],
|
||||
};
|
||||
if (this.opts.unit) {
|
||||
this.config.options.scales.yAxes = [{
|
||||
ticks: {
|
||||
callback: (n) => ComplexGraph.formatValue(n, this.opts.unit),
|
||||
}
|
||||
}];
|
||||
this.config.options.tooltips = {
|
||||
callbacks: {
|
||||
label: (tooltipItem, data) => {
|
||||
let label = data.datasets[tooltipItem.datasetIndex].label + ": ";
|
||||
let p = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
label += ComplexGraph.formatValue(p.y, this.opts.unit);
|
||||
return label;
|
||||
}
|
||||
xAxis: [
|
||||
{
|
||||
show: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
show: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
setData(d) {
|
||||
let datasets = (d);
|
||||
datasets.forEach((ds, i) => {
|
||||
let color = (i < this.opts.colors.length) ? this.opts.colors[i] : this.opts.colors[0];
|
||||
ds.backgroundColor = Chart.helpers.color(color).alpha(0.3).rgbString();
|
||||
ds.borderColor = color;
|
||||
ds.borderWidth = 2;
|
||||
ds.pointRadius = 2;
|
||||
this.chart.setOption({
|
||||
series: [
|
||||
{
|
||||
type: 'gauge',
|
||||
radius: '100%',
|
||||
center: ["50%", "58%"],
|
||||
max: d.value,
|
||||
axisLabel: { show: false },
|
||||
pointer: { show: false },
|
||||
detail: {
|
||||
offsetCenter: [0, 0],
|
||||
},
|
||||
data: [{ value: d.value }]
|
||||
}
|
||||
]
|
||||
});
|
||||
this.config.data.datasets = d;
|
||||
this.chart.update();
|
||||
}
|
||||
}
|
||||
Core.MatrixGraph = MatrixGraph;
|
||||
class GraphFactory {
|
||||
static create(elem) {
|
||||
let $elem = $(elem);
|
||||
let opts = {
|
||||
type: $elem.data("chart-type"),
|
||||
unit: $elem.data("chart-unit"),
|
||||
width: $elem.data("chart-width"),
|
||||
height: $elem.data("chart-height"),
|
||||
colors: $elem.data("chart-colors"),
|
||||
};
|
||||
Core.GaugeChart = GaugeChart;
|
||||
class VectorChart extends Chart {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
}
|
||||
config(opt) {
|
||||
$.extend(true, opt, {
|
||||
grid: {
|
||||
left: 20,
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params, index) => {
|
||||
return params.name + ": " + this.formatValue(params.value);
|
||||
},
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
show: false,
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
show: false,
|
||||
},
|
||||
],
|
||||
series: [{
|
||||
type: this.opts.type,
|
||||
radius: '80%',
|
||||
center: ['45%', '50%'],
|
||||
}],
|
||||
});
|
||||
}
|
||||
setData(d) {
|
||||
this.chart.setOption({
|
||||
legend: {
|
||||
data: d.legend,
|
||||
},
|
||||
series: [{
|
||||
data: d.data,
|
||||
}],
|
||||
});
|
||||
}
|
||||
}
|
||||
Core.VectorChart = VectorChart;
|
||||
class MatrixChart extends Chart {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
}
|
||||
config(opt) {
|
||||
$.extend(true, opt, {
|
||||
grid: {
|
||||
left: 60,
|
||||
top: 30,
|
||||
right: 30,
|
||||
bottom: 30,
|
||||
},
|
||||
tooltip: {
|
||||
formatter: (params) => {
|
||||
let html = params[0].axisValueLabel + '<br/>';
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
html += params[i].marker + params[i].seriesName + ': ' + this.formatValue(params[i].value[1]) + '<br/>';
|
||||
}
|
||||
return html;
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
setData(d) {
|
||||
d.series.forEach((s) => s.type = this.opts.type);
|
||||
this.chart.setOption({
|
||||
legend: {
|
||||
data: d.legend,
|
||||
},
|
||||
series: d.series,
|
||||
});
|
||||
}
|
||||
}
|
||||
Core.MatrixChart = MatrixChart;
|
||||
class ChartFactory {
|
||||
static create(opts) {
|
||||
switch (opts.type) {
|
||||
case "value":
|
||||
return new ValueGraph($elem, opts);
|
||||
case "gauge":
|
||||
return new GaugeChart(opts);
|
||||
case "line":
|
||||
case "bar":
|
||||
return new MatrixGraph($elem, opts);
|
||||
return new MatrixChart(opts);
|
||||
case "pie":
|
||||
return new VectorGraph($elem, opts);
|
||||
return new VectorChart(opts);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Core.GraphFactory = GraphFactory;
|
||||
class GraphPanelOptions {
|
||||
Core.ChartFactory = ChartFactory;
|
||||
class ChartDashboardOptions {
|
||||
constructor() {
|
||||
this.time = "30m";
|
||||
this.refreshInterval = 15000;
|
||||
this.period = 30;
|
||||
this.refreshInterval = 15;
|
||||
}
|
||||
}
|
||||
Core.GraphPanelOptions = GraphPanelOptions;
|
||||
class GraphPanel {
|
||||
constructor(elem, opts) {
|
||||
Core.ChartDashboardOptions = ChartDashboardOptions;
|
||||
class ChartDashboard {
|
||||
constructor(elem, charts, opts) {
|
||||
this.charts = [];
|
||||
this.opts = $.extend(new GraphPanelOptions(), opts);
|
||||
this.opts = $.extend(new ChartDashboardOptions(), opts);
|
||||
this.$panel = $(elem);
|
||||
this.$panel.children().each((i, e) => {
|
||||
let g = GraphFactory.create(e);
|
||||
if (g != null) {
|
||||
this.charts.push(g);
|
||||
}
|
||||
});
|
||||
charts.forEach(opts => this.createGraph(opts));
|
||||
Core.Dispatcher.bind(this.$panel).on("remove-chart", e => {
|
||||
let name = $(e.target).closest("div.column").data("chart-name");
|
||||
let name = $(e.target).closest("div.column").data("name");
|
||||
Core.Modal.confirm(`Are you sure to delete chart: <strong>${name}</strong>?`, "Delete chart", dlg => {
|
||||
this.removeGraph(name);
|
||||
dlg.close();
|
||||
@ -1357,85 +1385,70 @@ var Swirl;
|
||||
});
|
||||
$(window).resize(e => {
|
||||
$.each(this.charts, (i, g) => {
|
||||
g.resize(0, 0);
|
||||
g.resize();
|
||||
});
|
||||
});
|
||||
this.refreshData();
|
||||
}
|
||||
refreshData() {
|
||||
this.loadData();
|
||||
if (this.opts.refreshInterval > 0) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval);
|
||||
}
|
||||
this.refresh();
|
||||
}
|
||||
refresh() {
|
||||
if (!this.timer) {
|
||||
this.loadData();
|
||||
if (this.opts.refreshInterval > 0) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval);
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
refreshData() {
|
||||
this.loadData();
|
||||
if (this.opts.refreshInterval > 0) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval * 1000);
|
||||
}
|
||||
}
|
||||
stop() {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = 0;
|
||||
}
|
||||
setTime(time) {
|
||||
this.opts.time = time;
|
||||
setPeriod(period) {
|
||||
this.opts.period = period;
|
||||
this.loadData();
|
||||
}
|
||||
addGraph(c) {
|
||||
addGraph(opts) {
|
||||
this.createGraph(opts);
|
||||
this.loadData();
|
||||
}
|
||||
createGraph(opts) {
|
||||
for (let i = 0; i < this.charts.length; i++) {
|
||||
let chart = this.charts[i];
|
||||
if (chart.getName() === c.name) {
|
||||
if (chart.getOptions().name === opts.name) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let $chart = $(`<div class="column is-${c.width}" data-chart-name="${c.name}" data-chart-type="${c.type}" data-chart-unit="${c.unit}" data-chart-width="${c.width}" data-chart-height="${c.height}">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">${c.title}</p>
|
||||
<a data-action="remove-chart" class="card-header-icon" aria-label="remove chart">
|
||||
<span class="icon">
|
||||
<i class="fas fa-times has-text-danger" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div style="height: ${c.height}px">
|
||||
<canvas id="canvas_${c.name}"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`);
|
||||
this.$panel.append($chart);
|
||||
let g = GraphFactory.create($chart);
|
||||
if (g != null) {
|
||||
this.charts.push(g);
|
||||
let chart = ChartFactory.create(opts);
|
||||
if (chart != null) {
|
||||
this.$panel.append(chart.getElem());
|
||||
chart.init();
|
||||
this.charts.push(chart);
|
||||
}
|
||||
this.loadData();
|
||||
}
|
||||
removeGraph(name) {
|
||||
let index = -1;
|
||||
for (let i = 0; i < this.charts.length; i++) {
|
||||
let c = this.charts[i];
|
||||
if (c.getName() === name) {
|
||||
if (c.getOptions().name === name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index >= 0) {
|
||||
console.info(this.charts.length);
|
||||
let $elem = this.charts[index].getElem();
|
||||
this.charts.splice(index, 1);
|
||||
$elem.remove();
|
||||
console.info(this.charts.length);
|
||||
}
|
||||
}
|
||||
save() {
|
||||
let charts = this.charts.map(c => {
|
||||
return {
|
||||
name: c.getName(),
|
||||
name: c.getOptions().name,
|
||||
width: c.getOptions().width,
|
||||
height: c.getOptions().height,
|
||||
};
|
||||
@ -1456,37 +1469,37 @@ var Swirl;
|
||||
}
|
||||
loadData() {
|
||||
let args = {
|
||||
charts: this.charts.map(c => c.getName()).join(","),
|
||||
time: this.opts.time,
|
||||
charts: this.charts.map(c => c.getOptions().name).join(","),
|
||||
period: this.opts.period,
|
||||
};
|
||||
if (this.opts.key) {
|
||||
args.key = this.opts.key;
|
||||
}
|
||||
$ajax.get(`/system/chart/data`, args).json((d) => {
|
||||
$.each(this.charts, (i, g) => {
|
||||
if (d[g.getName()]) {
|
||||
g.setData(d[g.getName()]);
|
||||
if (d[g.getOptions().name]) {
|
||||
g.setData(d[g.getOptions().name]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Core.GraphPanel = GraphPanel;
|
||||
Core.ChartDashboard = ChartDashboard;
|
||||
})(Core = Swirl.Core || (Swirl.Core = {}));
|
||||
})(Swirl || (Swirl = {}));
|
||||
var Swirl;
|
||||
(function (Swirl) {
|
||||
var Modal = Swirl.Core.Modal;
|
||||
var GraphPanel = Swirl.Core.GraphPanel;
|
||||
var FilterBox = Swirl.Core.FilterBox;
|
||||
var ChartDashboard = Swirl.Core.ChartDashboard;
|
||||
class IndexPage {
|
||||
constructor() {
|
||||
this.fb = new FilterBox("#txt-query", this.filterCharts.bind(this));
|
||||
this.panel = new GraphPanel("#div-charts", { name: "home" });
|
||||
this.dashboard = new ChartDashboard("#div-charts", window.charts, { name: "home" });
|
||||
$("#btn-add").click(this.showAddDlg.bind(this));
|
||||
$("#btn-add-chart").click(this.addChart.bind(this));
|
||||
$("#btn-save").click(() => {
|
||||
this.panel.save();
|
||||
this.dashboard.save();
|
||||
});
|
||||
}
|
||||
showAddDlg() {
|
||||
@ -1531,7 +1544,7 @@ var Swirl;
|
||||
this.$charts.each((i, e) => {
|
||||
if ($(e).find(":checked").length > 0) {
|
||||
let c = this.charts[i];
|
||||
this.panel.addGraph(c);
|
||||
this.dashboard.addGraph(c);
|
||||
}
|
||||
});
|
||||
Modal.close();
|
||||
@ -2345,14 +2358,14 @@ var Swirl;
|
||||
var Service;
|
||||
(function (Service) {
|
||||
var Modal = Swirl.Core.Modal;
|
||||
var GraphPanel = Swirl.Core.GraphPanel;
|
||||
var ChartDashboard = Swirl.Core.ChartDashboard;
|
||||
class StatsPage {
|
||||
constructor() {
|
||||
let $cb_time = $("#cb-time");
|
||||
if ($cb_time.length == 0) {
|
||||
return;
|
||||
}
|
||||
this.panel = new GraphPanel("#div-charts", {
|
||||
this.dashboard = new ChartDashboard("#div-charts", window.charts, {
|
||||
name: "service",
|
||||
key: $("#h2-service-name").text()
|
||||
});
|
||||
@ -2363,14 +2376,14 @@ var Swirl;
|
||||
Modal.alert("Coming soon...");
|
||||
});
|
||||
$cb_time.change(e => {
|
||||
this.panel.setTime($(e.target).val());
|
||||
this.dashboard.setPeriod($(e.target).val());
|
||||
});
|
||||
$("#cb-refresh").change(e => {
|
||||
if ($(e.target).prop("checked")) {
|
||||
this.panel.refresh();
|
||||
this.dashboard.refresh();
|
||||
}
|
||||
else {
|
||||
this.panel.stop();
|
||||
this.dashboard.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
416
assets/swirl/ts/core/chart.ts
Normal file
416
assets/swirl/ts/core/chart.ts
Normal file
@ -0,0 +1,416 @@
|
||||
namespace Swirl.Core {
|
||||
export class ChartOptions {
|
||||
name: string;
|
||||
title: string;
|
||||
type?: string = "line";
|
||||
unit?: string;
|
||||
width?: number = 12;
|
||||
height?: number = 200;
|
||||
colors?: string[];
|
||||
}
|
||||
|
||||
export abstract class Chart {
|
||||
protected $elem: JQuery;
|
||||
protected chart: echarts.ECharts;
|
||||
protected opts?: ChartOptions;
|
||||
|
||||
protected constructor(opts: ChartOptions) {
|
||||
this.opts = $.extend(new ChartOptions(), opts);
|
||||
this.createElem();
|
||||
}
|
||||
|
||||
private createElem() {
|
||||
this.$elem = $(`<div class="column is-${this.opts.width}" data-name="${this.opts.name}">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">${this.opts.title}</p>
|
||||
<a data-action="remove-chart" class="card-header-icon" aria-label="remove chart">
|
||||
<span class="icon">
|
||||
<i class="fas fa-times has-text-danger" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
</header>
|
||||
<div class="card-content" style="height: ${this.opts.height}px"></div>
|
||||
</div>
|
||||
</div>`);
|
||||
}
|
||||
|
||||
init() {
|
||||
let opt = {
|
||||
legend: {
|
||||
x: 'right',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
// formatter: function (params) {
|
||||
// params = params[0];
|
||||
// var date = new Date(params.name);
|
||||
// return date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear() + ' : ' + params.value[1];
|
||||
// },
|
||||
axisPointer: {
|
||||
animation: false
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
splitLine: {show: false},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%'],
|
||||
splitLine: {show: false},
|
||||
axisLabel: {
|
||||
formatter: this.formatValue.bind(this),
|
||||
},
|
||||
},
|
||||
};
|
||||
this.config(opt);
|
||||
this.chart = echarts.init(<HTMLDivElement>this.$elem.find("div.card-content").get(0));
|
||||
this.chart.setOption(opt, true);
|
||||
}
|
||||
|
||||
getElem(): JQuery {
|
||||
return this.$elem;
|
||||
}
|
||||
|
||||
getOptions(): ChartOptions {
|
||||
return this.opts;
|
||||
}
|
||||
|
||||
resize() {
|
||||
this.chart.resize();
|
||||
}
|
||||
|
||||
abstract setData(d: any): void;
|
||||
|
||||
protected config(opt: echarts.EChartOption) {
|
||||
}
|
||||
|
||||
protected formatValue(value: number): string {
|
||||
switch (this.opts.unit) {
|
||||
case "percent:100":
|
||||
return value.toFixed(1) + "%";
|
||||
case "percent:1":
|
||||
return (value * 100).toFixed(1) + "%";
|
||||
case "time:s":
|
||||
if (value < 1) { // 1
|
||||
return (value * 1000).toFixed(0) + 'ms';
|
||||
} else {
|
||||
return value.toFixed(2) + 's';
|
||||
}
|
||||
case "size:bytes":
|
||||
if (value < 1024) { // 1K
|
||||
return value.toString() + 'B';
|
||||
} else if (value < 1048576) { // 1M
|
||||
return (value / 1024).toFixed(2) + 'K';
|
||||
} else if (value < 1073741824) { // 1G
|
||||
return (value / 1048576).toFixed(2) + 'M';
|
||||
} else {
|
||||
return (value / 1073741824).toFixed(2) + 'G';
|
||||
}
|
||||
default:
|
||||
return value.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gauge chart
|
||||
*/
|
||||
export class GaugeChart extends Chart {
|
||||
constructor(opts: ChartOptions) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
protected config(opt: echarts.EChartOption) {
|
||||
$.extend(true, opt, {
|
||||
grid: {
|
||||
left: 0,
|
||||
top: 20,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
show: false,
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
show: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
setData(d: any): void {
|
||||
this.chart.setOption({
|
||||
series: [
|
||||
{
|
||||
// name: d.name,
|
||||
type: 'gauge',
|
||||
radius: '100%',
|
||||
center: ["50%", "58%"], // 仪表位置
|
||||
max: d.value,
|
||||
axisLabel: {show: false},
|
||||
pointer: {show: false},
|
||||
detail: {
|
||||
offsetCenter: [0, 0],
|
||||
},
|
||||
data: [{value: d.value}]
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pie chart.
|
||||
*/
|
||||
export class VectorChart extends Chart {
|
||||
constructor(opts: ChartOptions) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
protected config(opt: echarts.EChartOption) {
|
||||
$.extend(true, opt, {
|
||||
grid: {
|
||||
left: 20,
|
||||
top: 20,
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: (params: any, index: number): string => {
|
||||
return params.name + ": " + this.formatValue(params.value);
|
||||
},
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
show: false,
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
show: false,
|
||||
},
|
||||
],
|
||||
series: [{
|
||||
type: this.opts.type,
|
||||
radius: '80%',
|
||||
center: ['45%', '50%'],
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
setData(d: any): void {
|
||||
this.chart.setOption({
|
||||
legend: {
|
||||
data: d.legend,
|
||||
},
|
||||
series: [{
|
||||
data: d.data,
|
||||
}],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Line/Bar etc.
|
||||
*/
|
||||
export class MatrixChart extends Chart {
|
||||
constructor(opts: ChartOptions) {
|
||||
super(opts);
|
||||
}
|
||||
|
||||
protected config(opt: echarts.EChartOption) {
|
||||
$.extend(true, opt, {
|
||||
grid: {
|
||||
left: 60,
|
||||
top: 30,
|
||||
right: 30,
|
||||
bottom: 30,
|
||||
},
|
||||
tooltip: {
|
||||
formatter: (params: any) => {
|
||||
let html = params[0].axisValueLabel + '<br/>';
|
||||
for (let i = 0; i < params.length; i++) {
|
||||
html += params[i].marker + params[i].seriesName +': ' + this.formatValue(params[i].value[1]) + '<br/>';
|
||||
}
|
||||
return html;
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setData(d: any): void {
|
||||
d.series.forEach((s: any) => s.type = this.opts.type);
|
||||
this.chart.setOption({
|
||||
legend: {
|
||||
data: d.legend,
|
||||
},
|
||||
series: d.series,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ChartFactory {
|
||||
static create(opts: ChartOptions): Chart {
|
||||
switch (opts.type) {
|
||||
case "gauge":
|
||||
return new GaugeChart(opts);
|
||||
case "line":
|
||||
case "bar":
|
||||
return new MatrixChart(opts);
|
||||
case "pie":
|
||||
return new VectorChart(opts);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class ChartDashboardOptions {
|
||||
name: string;
|
||||
key?: string;
|
||||
period?: number = 30;
|
||||
refreshInterval?: number = 15; // ms
|
||||
}
|
||||
|
||||
export class ChartDashboard {
|
||||
private $panel: JQuery;
|
||||
private opts: ChartDashboardOptions;
|
||||
private charts: Chart[] = [];
|
||||
private timer: number;
|
||||
|
||||
constructor(elem: string | Element | JQuery, charts: ChartOptions[], opts?: ChartDashboardOptions) {
|
||||
this.opts = $.extend(new ChartDashboardOptions(), opts);
|
||||
this.$panel = $(elem);
|
||||
charts.forEach(opts => this.createGraph(opts));
|
||||
|
||||
// events
|
||||
Dispatcher.bind(this.$panel).on("remove-chart", e => {
|
||||
let name = $(e.target).closest("div.column").data("name");
|
||||
Modal.confirm(`Are you sure to delete chart: <strong>${name}</strong>?`, "Delete chart", dlg => {
|
||||
this.removeGraph(name);
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
$(window).resize(e => {
|
||||
$.each(this.charts, (i: number, g: Chart) => {
|
||||
g.resize();
|
||||
});
|
||||
});
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
refresh() {
|
||||
if (!this.timer) {
|
||||
this.loadData();
|
||||
if (this.opts.refreshInterval > 0) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private refreshData() {
|
||||
this.loadData();
|
||||
if (this.opts.refreshInterval > 0) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = 0;
|
||||
}
|
||||
|
||||
setPeriod(period: number) {
|
||||
this.opts.period = period;
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
addGraph(opts: ChartOptions) {
|
||||
this.createGraph(opts);
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
private createGraph(opts: ChartOptions) {
|
||||
for (let i = 0; i < this.charts.length; i++) {
|
||||
let chart = this.charts[i];
|
||||
if (chart.getOptions().name === opts.name) {
|
||||
// chart already added.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let chart = ChartFactory.create(opts);
|
||||
if (chart != null) {
|
||||
this.$panel.append(chart.getElem());
|
||||
chart.init();
|
||||
this.charts.push(chart);
|
||||
}
|
||||
}
|
||||
|
||||
removeGraph(name: string) {
|
||||
let index = -1;
|
||||
for (let i = 0; i < this.charts.length; i++) {
|
||||
let c = this.charts[i];
|
||||
if (c.getOptions().name === name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
let $elem = this.charts[index].getElem();
|
||||
this.charts.splice(index, 1);
|
||||
$elem.remove();
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
let charts = this.charts.map(c => {
|
||||
return {
|
||||
name: c.getOptions().name,
|
||||
width: c.getOptions().width,
|
||||
height: c.getOptions().height,
|
||||
}
|
||||
});
|
||||
let args = {
|
||||
name: this.opts.name,
|
||||
key: this.opts.key || '',
|
||||
charts: charts,
|
||||
};
|
||||
$ajax.post(`/system/chart/save_dashboard`, args).json<AjaxResult>((r: AjaxResult) => {
|
||||
if (r.success) {
|
||||
Notification.show("success", "Successfully saved.");
|
||||
} else {
|
||||
Notification.show("danger", r.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private loadData() {
|
||||
let args: any = {
|
||||
charts: this.charts.map(c => c.getOptions().name).join(","),
|
||||
period: this.opts.period,
|
||||
};
|
||||
if (this.opts.key) {
|
||||
args.key = this.opts.key;
|
||||
}
|
||||
$ajax.get(`/system/chart/data`, args).json((d: { [index: string]: any[] }) => {
|
||||
$.each(this.charts, (i: number, g: Chart) => {
|
||||
if (d[g.getOptions().name]) {
|
||||
g.setData(d[g.getOptions().name]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,461 +0,0 @@
|
||||
namespace Swirl.Core {
|
||||
export class GraphOptions {
|
||||
type?: string = "line";
|
||||
unit?: string;
|
||||
width?: number = 12;
|
||||
height?: number = 150;
|
||||
colors?: string[];
|
||||
}
|
||||
|
||||
export abstract class Graph {
|
||||
protected $elem: JQuery;
|
||||
protected opts?: GraphOptions;
|
||||
private readonly name: string;
|
||||
|
||||
protected constructor(elem: string | Element | JQuery, opts?: GraphOptions) {
|
||||
this.$elem = $(elem);
|
||||
this.opts = $.extend(new GraphOptions(), opts);
|
||||
this.name = this.$elem.data("chart-name");
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
getElem(): JQuery {
|
||||
return this.$elem;
|
||||
}
|
||||
|
||||
getOptions(): GraphOptions {
|
||||
return this.opts;
|
||||
}
|
||||
|
||||
abstract resize(w: number, h: number): void;
|
||||
|
||||
abstract setData(d: any): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple value
|
||||
*/
|
||||
export class ValueGraph extends Graph {
|
||||
private $canvas: JQuery;
|
||||
|
||||
constructor(elem: string | Element | JQuery, opts?: GraphOptions) {
|
||||
super(elem, opts);
|
||||
}
|
||||
|
||||
setData(d: any): void {
|
||||
}
|
||||
|
||||
resize(w: number, h: number): void {
|
||||
}
|
||||
}
|
||||
|
||||
export class ComplexGraph extends Graph {
|
||||
protected chart: Chart;
|
||||
protected ctx: CanvasRenderingContext2D;
|
||||
protected config: Chart.ChartConfiguration;
|
||||
protected static defaultColors = [
|
||||
'rgb(255, 99, 132)', // red
|
||||
'rgb(75, 192, 192)', // green
|
||||
'rgb(255, 159, 64)', // orange
|
||||
'rgb(54, 162, 235)', // blue
|
||||
'rgb(153, 102, 255)', // purple
|
||||
'rgb(255, 205, 86)', // yellow
|
||||
'rgb(201, 203, 207)', // grey
|
||||
];
|
||||
|
||||
constructor(elem: string | Element | JQuery, opts?: GraphOptions) {
|
||||
super(elem, opts);
|
||||
|
||||
if (!this.opts.colors) {
|
||||
this.opts.colors = ComplexGraph.defaultColors;
|
||||
}
|
||||
|
||||
this.config = {
|
||||
type: opts.type,
|
||||
data: {},
|
||||
options: {
|
||||
responsive: false,
|
||||
maintainAspectRatio: false,
|
||||
// title: {
|
||||
// // display: true,
|
||||
// text: opts.title || 'NONE'
|
||||
// },
|
||||
// legend: {
|
||||
// position: "bottom"
|
||||
// },
|
||||
animation: {
|
||||
duration: 0,
|
||||
// easing: 'easeOutBounce',
|
||||
},
|
||||
// tooltips: {
|
||||
// callbacks: {
|
||||
// label: function (tooltipItem: Chart.ChartTooltipItem, data: Chart.ChartData) {
|
||||
// let label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||
// if (label) {
|
||||
// label += ': ';
|
||||
// }
|
||||
//
|
||||
// let p = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
// if (typeof p == "number") {
|
||||
// label += ComplexGraph.formatValue(p, opts.unit);
|
||||
// } else {
|
||||
// label += ComplexGraph.formatValue(p.y, opts.unit);
|
||||
// }
|
||||
// return label;
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.fillConfig();
|
||||
|
||||
this.ctx = (<HTMLCanvasElement>$(elem).find("canvas").get(0)).getContext('2d');
|
||||
if (opts.height) {
|
||||
this.ctx.canvas.width = this.ctx.canvas.parentElement.offsetWidth;
|
||||
this.ctx.canvas.height = this.ctx.canvas.parentElement.offsetHeight;
|
||||
}
|
||||
this.chart = new Chart(this.ctx, this.config);
|
||||
}
|
||||
|
||||
setData(d: any): void {
|
||||
}
|
||||
|
||||
resize(w: number, h: number): void {
|
||||
// this.ctx.canvas.style.width = this.ctx.canvas.parentElement.offsetWidth + "px";
|
||||
// this.ctx.canvas.style.height = this.ctx.canvas.parentElement.offsetWidth + "px";
|
||||
this.ctx.canvas.width = this.ctx.canvas.parentElement.offsetWidth;
|
||||
this.ctx.canvas.height = this.ctx.canvas.parentElement.offsetHeight;
|
||||
this.chart.resize();
|
||||
}
|
||||
|
||||
protected fillConfig() {
|
||||
}
|
||||
|
||||
protected static formatValue(value: number, unit: string): string {
|
||||
switch (unit) {
|
||||
case "percent:100":
|
||||
return value.toFixed(1) + "%";
|
||||
case "percent:1":
|
||||
return (value * 100).toFixed(1) + "%";
|
||||
case "time:s":
|
||||
if (value < 1) { // 1
|
||||
return (value * 1000).toFixed(0) + 'ms';
|
||||
} else {
|
||||
return value.toFixed(2) + 's';
|
||||
}
|
||||
case "size:bytes":
|
||||
if (value < 1024) { // 1K
|
||||
return value.toString() + 'B';
|
||||
} else if (value < 1048576) { // 1M
|
||||
return (value / 1024).toFixed(2) + 'K';
|
||||
} else if (value < 1073741824) { // 1G
|
||||
return (value / 1048576).toFixed(2) + 'M';
|
||||
} else {
|
||||
return (value / 1073741824).toFixed(2) + 'G';
|
||||
}
|
||||
default:
|
||||
return value.toFixed(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pie etc.
|
||||
*/
|
||||
export class VectorGraph extends ComplexGraph {
|
||||
protected fillConfig() {
|
||||
this.config.options.legend = {
|
||||
position: "right"
|
||||
};
|
||||
this.config.options.tooltips = {
|
||||
callbacks: {
|
||||
label: (tooltipItem: Chart.ChartTooltipItem, data: Chart.ChartData): string => {
|
||||
let label = data.labels[tooltipItem.index] + ": ";
|
||||
let p = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
if (typeof p == "number") {
|
||||
label += ComplexGraph.formatValue(p, this.opts.unit);
|
||||
}
|
||||
return label;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
setData(d: any): void {
|
||||
// data = {
|
||||
// datasets: [{
|
||||
// data: [10, 20, 30]
|
||||
// }],
|
||||
//
|
||||
// // These labels appear in the legend and in the tooltips when hovering different arcs
|
||||
// labels: [
|
||||
// 'Red',
|
||||
// 'Yellow',
|
||||
// 'Blue'
|
||||
// ]
|
||||
// };
|
||||
this.config.data.datasets = [{
|
||||
data: d.data,
|
||||
backgroundColor: this.opts.colors,
|
||||
}];
|
||||
this.config.data.labels = d.labels;
|
||||
// this.config.data.datasets = [{
|
||||
// data: [10, 20, 30],
|
||||
// backgroundColor: this.opts.colors,
|
||||
// }];
|
||||
// this.config.data.labels = [
|
||||
// 'Red',
|
||||
// 'Yellow',
|
||||
// 'Blue'
|
||||
// ];
|
||||
this.chart.update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Line/Bar etc.
|
||||
*/
|
||||
export class MatrixGraph extends ComplexGraph {
|
||||
constructor(elem: string | Element | JQuery, opts?: GraphOptions) {
|
||||
super(elem, opts);
|
||||
}
|
||||
|
||||
protected fillConfig() {
|
||||
this.config.options.scales = {
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
time: {
|
||||
// min: new Date(),
|
||||
// max: new Date(),
|
||||
unit: 'minute',
|
||||
tooltipFormat: 'YYYY/MM/DD HH:mm:ss',
|
||||
displayFormats: {
|
||||
minute: 'HH:mm'
|
||||
}
|
||||
},
|
||||
}],
|
||||
};
|
||||
if (this.opts.unit) {
|
||||
this.config.options.scales.yAxes = [{
|
||||
ticks: {
|
||||
callback: (n: number) => ComplexGraph.formatValue(n, this.opts.unit),
|
||||
}
|
||||
}];
|
||||
this.config.options.tooltips = {
|
||||
callbacks: {
|
||||
label: (tooltipItem: Chart.ChartTooltipItem, data: Chart.ChartData): string => {
|
||||
let label = data.datasets[tooltipItem.datasetIndex].label + ": ";
|
||||
let p: any = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||||
label += ComplexGraph.formatValue(p.y, this.opts.unit);
|
||||
return label;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setData(d: any): void {
|
||||
let datasets = <Chart.ChartDataSets[]>(d);
|
||||
datasets.forEach((ds, i) => {
|
||||
let color = (i < this.opts.colors.length) ? this.opts.colors[i] : this.opts.colors[0];
|
||||
ds.backgroundColor = Chart.helpers.color(color).alpha(0.3).rgbString();
|
||||
ds.borderColor = color;
|
||||
ds.borderWidth = 2;
|
||||
ds.pointRadius = 2;
|
||||
// ds.fill = false;
|
||||
});
|
||||
this.config.data.datasets = d;
|
||||
this.chart.update();
|
||||
}
|
||||
}
|
||||
|
||||
export class GraphFactory {
|
||||
static create(elem: string | Element | JQuery): Graph {
|
||||
let $elem = $(elem);
|
||||
let opts: GraphOptions = {
|
||||
type: $elem.data("chart-type"),
|
||||
unit: $elem.data("chart-unit"),
|
||||
width: $elem.data("chart-width"),
|
||||
height: $elem.data("chart-height"),
|
||||
colors: $elem.data("chart-colors"),
|
||||
};
|
||||
switch (opts.type) {
|
||||
case "value":
|
||||
return new ValueGraph($elem, opts);
|
||||
case "line":
|
||||
case "bar":
|
||||
return new MatrixGraph($elem, opts);
|
||||
case "pie":
|
||||
return new VectorGraph($elem, opts);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class GraphPanelOptions {
|
||||
name: string;
|
||||
key?: string;
|
||||
time?: string = "30m";
|
||||
refreshInterval?: number = 15000; // ms
|
||||
}
|
||||
|
||||
export class GraphPanel {
|
||||
private $panel: JQuery;
|
||||
private opts: GraphPanelOptions;
|
||||
private charts: Graph[] = [];
|
||||
private timer: number;
|
||||
|
||||
constructor(elem: string | Element | JQuery, opts?: GraphPanelOptions) {
|
||||
this.opts = $.extend(new GraphPanelOptions(), opts);
|
||||
this.$panel = $(elem);
|
||||
this.$panel.children().each((i, e) => {
|
||||
let g = GraphFactory.create(e);
|
||||
if (g != null) {
|
||||
this.charts.push(g);
|
||||
}
|
||||
});
|
||||
|
||||
Dispatcher.bind(this.$panel).on("remove-chart", e => {
|
||||
let name = $(e.target).closest("div.column").data("chart-name");
|
||||
Modal.confirm(`Are you sure to delete chart: <strong>${name}</strong>?`, "Delete chart", dlg => {
|
||||
this.removeGraph(name);
|
||||
dlg.close();
|
||||
});
|
||||
});
|
||||
$(window).resize(e => {
|
||||
$.each(this.charts, (i: number, g: Graph) => {
|
||||
g.resize(0, 0);
|
||||
});
|
||||
});
|
||||
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
private refreshData() {
|
||||
this.loadData();
|
||||
if (this.opts.refreshInterval > 0) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval);
|
||||
}
|
||||
}
|
||||
|
||||
refresh() {
|
||||
if (!this.timer) {
|
||||
this.loadData();
|
||||
if (this.opts.refreshInterval > 0) {
|
||||
this.timer = setTimeout(this.refreshData.bind(this), this.opts.refreshInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = 0;
|
||||
}
|
||||
|
||||
setTime(time: string) {
|
||||
this.opts.time = time;
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
addGraph(c: any) {
|
||||
for (let i = 0; i < this.charts.length; i++) {
|
||||
let chart = this.charts[i];
|
||||
if (chart.getName() === c.name) {
|
||||
// chart already added.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let $chart = $(`<div class="column is-${c.width}" data-chart-name="${c.name}" data-chart-type="${c.type}" data-chart-unit="${c.unit}" data-chart-width="${c.width}" data-chart-height="${c.height}">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">${c.title}</p>
|
||||
<a data-action="remove-chart" class="card-header-icon" aria-label="remove chart">
|
||||
<span class="icon">
|
||||
<i class="fas fa-times has-text-danger" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div style="height: ${c.height}px">
|
||||
<canvas id="canvas_${c.name}"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`);
|
||||
this.$panel.append($chart);
|
||||
|
||||
let g = GraphFactory.create($chart);
|
||||
if (g != null) {
|
||||
this.charts.push(g);
|
||||
}
|
||||
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
removeGraph(name: string) {
|
||||
// todo:
|
||||
let index = -1;
|
||||
for (let i = 0; i < this.charts.length; i++) {
|
||||
let c = this.charts[i];
|
||||
if (c.getName() === name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
console.info(this.charts.length);
|
||||
let $elem = this.charts[index].getElem();
|
||||
this.charts.splice(index, 1);
|
||||
$elem.remove();
|
||||
console.info(this.charts.length);
|
||||
}
|
||||
}
|
||||
|
||||
save() {
|
||||
let charts = this.charts.map(c => {
|
||||
return {
|
||||
name: c.getName(),
|
||||
width: c.getOptions().width,
|
||||
height: c.getOptions().height,
|
||||
// colors: ,
|
||||
}
|
||||
});
|
||||
let args = {
|
||||
name: this.opts.name,
|
||||
key: this.opts.key || '',
|
||||
charts: charts,
|
||||
};
|
||||
$ajax.post(`/system/chart/save_dashboard`, args).json<AjaxResult>((r: AjaxResult) => {
|
||||
if (r.success) {
|
||||
Notification.show("success", "Successfully saved.");
|
||||
} else {
|
||||
Notification.show("danger", r.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private loadData() {
|
||||
let args: any = {
|
||||
charts: this.charts.map(c => c.getName()).join(","),
|
||||
time: this.opts.time,
|
||||
};
|
||||
if (this.opts.key) {
|
||||
args.key = this.opts.key;
|
||||
}
|
||||
$ajax.get(`/system/chart/data`, args).json((d: { [index: string]: Chart.ChartDataSets[] }) => {
|
||||
$.each(this.charts, (i: number, g: Graph) => {
|
||||
if (d[g.getName()]) {
|
||||
g.setData(d[g.getName()]);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
///<reference path="core/core.ts" />
|
||||
///<reference path="core/graph.ts" />
|
||||
///<reference path="core/chart.ts" />
|
||||
namespace Swirl {
|
||||
import Modal = Swirl.Core.Modal;
|
||||
import GraphPanel = Swirl.Core.GraphPanel;
|
||||
import FilterBox = Swirl.Core.FilterBox;
|
||||
import ChartDashboard = Swirl.Core.ChartDashboard;
|
||||
|
||||
export class IndexPage {
|
||||
private panel: GraphPanel;
|
||||
private dashboard: ChartDashboard;
|
||||
private fb: FilterBox;
|
||||
private charts: any;
|
||||
private $charts: JQuery;
|
||||
|
||||
constructor() {
|
||||
this.fb = new FilterBox("#txt-query", this.filterCharts.bind(this));
|
||||
this.panel = new GraphPanel("#div-charts", {name: "home"});
|
||||
this.dashboard = new ChartDashboard("#div-charts", window.charts, {name: "home"});
|
||||
$("#btn-add").click(this.showAddDlg.bind(this));
|
||||
$("#btn-add-chart").click(this.addChart.bind(this));
|
||||
$("#btn-save").click(() => {
|
||||
this.panel.save();
|
||||
this.dashboard.save();
|
||||
});
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ namespace Swirl {
|
||||
this.$charts.each((i, e) => {
|
||||
if ($(e).find(":checked").length > 0) {
|
||||
let c = this.charts[i];
|
||||
this.panel.addGraph(c);
|
||||
this.dashboard.addGraph(c);
|
||||
}
|
||||
});
|
||||
Modal.close();
|
||||
|
@ -1,11 +1,11 @@
|
||||
///<reference path="../core/core.ts" />
|
||||
///<reference path="../core/graph.ts" />
|
||||
///<reference path="../core/chart.ts" />
|
||||
namespace Swirl.Service {
|
||||
import Modal = Swirl.Core.Modal;
|
||||
import GraphPanel = Swirl.Core.GraphPanel;
|
||||
import ChartDashboard = Swirl.Core.ChartDashboard;
|
||||
|
||||
export class StatsPage {
|
||||
private panel: GraphPanel;
|
||||
private dashboard: ChartDashboard;
|
||||
|
||||
constructor() {
|
||||
let $cb_time = $("#cb-time");
|
||||
@ -13,7 +13,7 @@ namespace Swirl.Service {
|
||||
return;
|
||||
}
|
||||
|
||||
this.panel = new GraphPanel("#div-charts", {
|
||||
this.dashboard = new ChartDashboard("#div-charts", window.charts, {
|
||||
name: "service",
|
||||
key: $("#h2-service-name").text()
|
||||
});
|
||||
@ -25,13 +25,13 @@ namespace Swirl.Service {
|
||||
Modal.alert("Coming soon...");
|
||||
});
|
||||
$cb_time.change(e => {
|
||||
this.panel.setTime($(e.target).val());
|
||||
this.dashboard.setPeriod($(e.target).val());
|
||||
});
|
||||
$("#cb-refresh").change(e => {
|
||||
if ($(e.target).prop("checked")) {
|
||||
this.panel.refresh();
|
||||
this.dashboard.refresh();
|
||||
} else {
|
||||
this.panel.stop();
|
||||
this.dashboard.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
590
assets/swirl/typings/chart.d.ts
vendored
590
assets/swirl/typings/chart.d.ts
vendored
@ -1,590 +0,0 @@
|
||||
// Type definitions for Chart.js 2.7
|
||||
// Project: https://github.com/nnnick/Chart.js
|
||||
// Definitions by: Alberto Nuti <https://github.com/anuti>
|
||||
// Fabien Lavocat <https://github.com/FabienLavocat>
|
||||
// KentarouTakeda <https://github.com/KentarouTakeda>
|
||||
// Larry Bahr <https://github.com/larrybahr>
|
||||
// Daniel Luz <https://github.com/mernen>
|
||||
// Joseph Page <https://github.com/josefpaij>
|
||||
// Dan Manastireanu <https://github.com/danmana>
|
||||
// Guillaume Rodriguez <https://github.com/guillaume-ro-fr>
|
||||
// Sergey Rubanov <https://github.com/chicoxyzzy>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.3
|
||||
|
||||
declare class Chart {
|
||||
static readonly Chart: typeof Chart;
|
||||
constructor(
|
||||
context: string | CanvasRenderingContext2D | HTMLCanvasElement | ArrayLike<CanvasRenderingContext2D | HTMLCanvasElement>,
|
||||
options: Chart.ChartConfiguration
|
||||
);
|
||||
config: Chart.ChartConfiguration;
|
||||
data: Chart.ChartData;
|
||||
destroy: () => {};
|
||||
update: (duration?: any, lazy?: any) => {};
|
||||
render: (duration?: any, lazy?: any) => {};
|
||||
stop: () => {};
|
||||
resize: () => {};
|
||||
clear: () => {};
|
||||
toBase64: () => string;
|
||||
generateLegend: () => {};
|
||||
getElementAtEvent: (e: any) => {};
|
||||
getElementsAtEvent: (e: any) => Array<{}>;
|
||||
getDatasetAtEvent: (e: any) => Array<{}>;
|
||||
ctx: CanvasRenderingContext2D|null;
|
||||
canvas: HTMLCanvasElement|null;
|
||||
chartArea: Chart.ChartArea;
|
||||
static pluginService: PluginServiceStatic;
|
||||
|
||||
static defaults: {
|
||||
global: Chart.ChartOptions & Chart.ChartFontOptions;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
static controllers: {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
// Tooltip Static Options
|
||||
static Tooltip: Chart.ChartTooltipsStaticConfiguration;
|
||||
static helpers: HelperStatic;
|
||||
}
|
||||
declare class PluginServiceStatic {
|
||||
register(plugin: PluginServiceRegistrationOptions): void;
|
||||
unregister(plugin: PluginServiceRegistrationOptions): void;
|
||||
}
|
||||
declare class HelperStatic {
|
||||
color(color: string): Color;
|
||||
}
|
||||
declare class Color {
|
||||
alpha(alpha: number): Color;
|
||||
rgbString(): string
|
||||
}
|
||||
|
||||
interface PluginServiceRegistrationOptions {
|
||||
beforeInit?(chartInstance: Chart): void;
|
||||
afterInit?(chartInstance: Chart): void;
|
||||
|
||||
resize?(chartInstance: Chart, newChartSize: Size): void;
|
||||
|
||||
beforeUpdate?(chartInstance: Chart): void;
|
||||
afterScaleUpdate?(chartInstance: Chart): void;
|
||||
beforeDatasetsUpdate?(chartInstance: Chart): void;
|
||||
afterDatasetsUpdate?(chartInstance: Chart): void;
|
||||
afterUpdate?(chartInstance: Chart): void;
|
||||
|
||||
// This is called at the start of a render. It is only called once, even if the animation will run for a number of frames. Use beforeDraw or afterDraw
|
||||
// to do something on each animation frame
|
||||
beforeRender?(chartInstance: Chart): void;
|
||||
|
||||
// Easing is for animation
|
||||
beforeDraw?(chartInstance: Chart, easing: string): void;
|
||||
afterDraw?(chartInstance: Chart, easing: string): void;
|
||||
// Before the datasets are drawn but after scales are drawn
|
||||
beforeDatasetsDraw?(chartInstance: Chart, easing: string): void;
|
||||
afterDatasetsDraw?(chartInstance: Chart, easing: string): void;
|
||||
|
||||
// Called before drawing the `tooltip`. If any plugin returns `false`,
|
||||
// the tooltip drawing is cancelled until another `render` is triggered.
|
||||
beforeTooltipDraw?(chartInstance: Chart): void;
|
||||
// Called after drawing the `tooltip`. Note that this hook will not,
|
||||
// be called if the tooltip drawing has been previously cancelled.
|
||||
afterTooltipDraw?(chartInstance: Chart): void;
|
||||
|
||||
destroy?(chartInstance: Chart): void;
|
||||
|
||||
// Called when an event occurs on the chart
|
||||
beforeEvent?(chartInstance: Chart, event: Event): void;
|
||||
afterEvent?(chartInstance: Chart, event: Event): void;
|
||||
}
|
||||
|
||||
interface Size {
|
||||
height: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
declare namespace Chart {
|
||||
type ChartType = 'line' | 'bar' | 'radar' | 'doughnut' | 'polarArea' | 'bubble' | 'pie';
|
||||
|
||||
type TimeUnit = 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year';
|
||||
|
||||
type ScaleType = 'category' | 'linear' | 'logarithmic' | 'time' | 'radialLinear';
|
||||
|
||||
type PointStyle = 'circle' | 'cross' | 'crossRot' | 'dash' | 'line' | 'rect' | 'rectRounded' | 'rectRot' | 'star' | 'triangle';
|
||||
|
||||
type PositionType = 'left' | 'right' | 'top' | 'bottom';
|
||||
|
||||
interface ChartArea {
|
||||
top: number;
|
||||
right: number;
|
||||
bottom: number;
|
||||
left: number;
|
||||
}
|
||||
|
||||
interface ChartLegendItem {
|
||||
text?: string;
|
||||
fillStyle?: string;
|
||||
hidden?: boolean;
|
||||
lineCap?: string;
|
||||
lineDash?: number[];
|
||||
lineDashOffset?: number;
|
||||
lineJoin?: string;
|
||||
lineWidth?: number;
|
||||
strokeStyle?: string;
|
||||
pointStyle?: PointStyle;
|
||||
}
|
||||
|
||||
interface ChartTooltipItem {
|
||||
xLabel?: string;
|
||||
yLabel?: string;
|
||||
datasetIndex?: number;
|
||||
index?: number;
|
||||
}
|
||||
|
||||
interface ChartTooltipLabelColor {
|
||||
borderColor: ChartColor;
|
||||
backgroundColor: ChartColor;
|
||||
}
|
||||
|
||||
interface ChartTooltipCallback {
|
||||
beforeTitle?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||
title?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||
afterTitle?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||
beforeBody?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||
beforeLabel?(tooltipItem: ChartTooltipItem, data: ChartData): string | string[];
|
||||
label?(tooltipItem: ChartTooltipItem, data: ChartData): string | string[];
|
||||
labelColor?(tooltipItem: ChartTooltipItem, chart: Chart): ChartTooltipLabelColor;
|
||||
labelTextColor?(tooltipItem: ChartTooltipItem, chart: Chart): string;
|
||||
afterLabel?(tooltipItem: ChartTooltipItem, data: ChartData): string | string[];
|
||||
afterBody?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||
beforeFooter?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||
footer?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||
afterFooter?(item: ChartTooltipItem[], data: ChartData): string | string[];
|
||||
}
|
||||
|
||||
interface ChartAnimationParameter {
|
||||
chartInstance?: any;
|
||||
animationObject?: any;
|
||||
}
|
||||
|
||||
interface ChartPoint {
|
||||
x?: number | string | Date;
|
||||
y?: number;
|
||||
r?: number;
|
||||
}
|
||||
|
||||
interface ChartConfiguration {
|
||||
type?: ChartType | string;
|
||||
data?: ChartData;
|
||||
options?: ChartOptions;
|
||||
// Plugins can require any options
|
||||
plugins?: any;
|
||||
}
|
||||
|
||||
interface ChartData {
|
||||
labels?: Array<string | string[]>;
|
||||
datasets?: ChartDataSets[];
|
||||
}
|
||||
|
||||
interface ChartOptions {
|
||||
responsive?: boolean;
|
||||
responsiveAnimationDuration?: number;
|
||||
aspectRatio?: number;
|
||||
maintainAspectRatio?: boolean;
|
||||
events?: string[];
|
||||
onClick?(event?: MouseEvent, activeElements?: Array<{}>): any;
|
||||
title?: ChartTitleOptions;
|
||||
legend?: ChartLegendOptions;
|
||||
tooltips?: ChartTooltipOptions;
|
||||
hover?: ChartHoverOptions;
|
||||
animation?: ChartAnimationOptions;
|
||||
elements?: ChartElementsOptions;
|
||||
layout?: ChartLayoutOptions;
|
||||
scales?: ChartScales;
|
||||
showLines?: boolean;
|
||||
spanGaps?: boolean;
|
||||
cutoutPercentage?: number;
|
||||
circumference?: number;
|
||||
rotation?: number;
|
||||
}
|
||||
|
||||
interface ChartFontOptions {
|
||||
defaultFontColor?: ChartColor;
|
||||
defaultFontFamily?: string;
|
||||
defaultFontSize?: number;
|
||||
defaultFontStyle?: string;
|
||||
}
|
||||
|
||||
interface ChartTitleOptions {
|
||||
display?: boolean;
|
||||
position?: PositionType;
|
||||
fullWdith?: boolean;
|
||||
fontSize?: number;
|
||||
fontFamily?: string;
|
||||
fontColor?: ChartColor;
|
||||
fontStyle?: string;
|
||||
padding?: number;
|
||||
text?: string;
|
||||
}
|
||||
|
||||
interface ChartLegendOptions {
|
||||
display?: boolean;
|
||||
position?: PositionType;
|
||||
fullWidth?: boolean;
|
||||
onClick?(event: MouseEvent, legendItem: ChartLegendItem): void;
|
||||
onHover?(event: MouseEvent, legendItem: ChartLegendItem): void;
|
||||
labels?: ChartLegendLabelOptions;
|
||||
reverse?: boolean;
|
||||
}
|
||||
|
||||
interface ChartLegendLabelOptions {
|
||||
boxWidth?: number;
|
||||
fontSize?: number;
|
||||
fontStyle?: string;
|
||||
fontColor?: ChartColor;
|
||||
fontFamily?: string;
|
||||
padding?: number;
|
||||
generateLabels?(chart: any): any;
|
||||
filter?(item: ChartLegendItem, data: ChartData): any;
|
||||
usePointStyle?: boolean;
|
||||
}
|
||||
|
||||
interface ChartTooltipOptions {
|
||||
enabled?: boolean;
|
||||
custom?(a: any): void;
|
||||
mode?: string;
|
||||
intersect?: boolean;
|
||||
backgroundColor?: ChartColor;
|
||||
titleFontFamily?: string;
|
||||
titleFontSize?: number;
|
||||
titleFontStyle?: string;
|
||||
titleFontColor?: ChartColor;
|
||||
titleSpacing?: number;
|
||||
titleMarginBottom?: number;
|
||||
bodyFontFamily?: string;
|
||||
bodyFontSize?: number;
|
||||
bodyFontStyle?: string;
|
||||
bodyFontColor?: ChartColor;
|
||||
bodySpacing?: number;
|
||||
footerFontFamily?: string;
|
||||
footerFontSize?: number;
|
||||
footerFontStyle?: string;
|
||||
footerFontColor?: ChartColor;
|
||||
footerSpacing?: number;
|
||||
footerMarginTop?: number;
|
||||
xPadding?: number;
|
||||
yPadding?: number;
|
||||
caretSize?: number;
|
||||
cornerRadius?: number;
|
||||
multiKeyBackground?: string;
|
||||
callbacks?: ChartTooltipCallback;
|
||||
filter?(item: ChartTooltipItem): boolean;
|
||||
itemSort?(itemA: ChartTooltipItem, itemB: ChartTooltipItem): number;
|
||||
position?: string;
|
||||
caretPadding?: number;
|
||||
displayColors?: boolean;
|
||||
borderColor?: ChartColor;
|
||||
borderWidth?: number;
|
||||
}
|
||||
|
||||
interface ChartTooltipsStaticConfiguration {
|
||||
positioners: {[mode: string]: ChartTooltipPositioner};
|
||||
}
|
||||
|
||||
type ChartTooltipPositioner = (elements: any[], eventPosition: Point) => Point;
|
||||
|
||||
interface ChartHoverOptions {
|
||||
mode?: string;
|
||||
animationDuration?: number;
|
||||
intersect?: boolean;
|
||||
onHover?(active: any): void;
|
||||
}
|
||||
|
||||
interface ChartAnimationObject {
|
||||
currentStep?: number;
|
||||
numSteps?: number;
|
||||
easing?: string;
|
||||
render?(arg: any): void;
|
||||
onAnimationProgress?(arg: any): void;
|
||||
onAnimationComplete?(arg: any): void;
|
||||
}
|
||||
|
||||
interface ChartAnimationOptions {
|
||||
duration?: number;
|
||||
easing?: string;
|
||||
onProgress?(chart: any): void;
|
||||
onComplete?(chart: any): void;
|
||||
}
|
||||
|
||||
interface ChartElementsOptions {
|
||||
point?: ChartPointOptions;
|
||||
line?: ChartLineOptions;
|
||||
arc?: ChartArcOptions;
|
||||
rectangle?: ChartRectangleOptions;
|
||||
}
|
||||
|
||||
interface ChartArcOptions {
|
||||
backgroundColor?: ChartColor;
|
||||
borderColor?: ChartColor;
|
||||
borderWidth?: number;
|
||||
}
|
||||
|
||||
interface ChartLineOptions {
|
||||
tension?: number;
|
||||
backgroundColor?: ChartColor;
|
||||
borderWidth?: number;
|
||||
borderColor?: ChartColor;
|
||||
borderCapStyle?: string;
|
||||
borderDash?: any[];
|
||||
borderDashOffset?: number;
|
||||
borderJoinStyle?: string;
|
||||
capBezierPoints?: boolean;
|
||||
fill?: 'zero' | 'top' | 'bottom' | boolean;
|
||||
stepped?: boolean;
|
||||
}
|
||||
|
||||
interface ChartPointOptions {
|
||||
radius?: number;
|
||||
pointStyle?: PointStyle;
|
||||
backgroundColor?: ChartColor;
|
||||
borderWidth?: number;
|
||||
borderColor?: ChartColor;
|
||||
hitRadius?: number;
|
||||
hoverRadius?: number;
|
||||
hoverBorderWidth?: number;
|
||||
}
|
||||
|
||||
interface ChartRectangleOptions {
|
||||
backgroundColor?: ChartColor;
|
||||
borderWidth?: number;
|
||||
borderColor?: ChartColor;
|
||||
borderSkipped?: string;
|
||||
}
|
||||
|
||||
interface ChartLayoutOptions {
|
||||
padding?: ChartLayoutPaddingObject | number;
|
||||
}
|
||||
|
||||
interface ChartLayoutPaddingObject {
|
||||
top?: number;
|
||||
right?: number;
|
||||
bottom?: number;
|
||||
left?: number;
|
||||
}
|
||||
|
||||
interface GridLineOptions {
|
||||
display?: boolean;
|
||||
color?: ChartColor;
|
||||
borderDash?: number[];
|
||||
borderDashOffset?: number;
|
||||
lineWidth?: number;
|
||||
drawBorder?: boolean;
|
||||
drawOnChartArea?: boolean;
|
||||
drawTicks?: boolean;
|
||||
tickMarkLength?: number;
|
||||
zeroLineWidth?: number;
|
||||
zeroLineColor?: ChartColor;
|
||||
zeroLineBorderDash?: number[];
|
||||
zeroLineBorderDashOffset?: number;
|
||||
offsetGridLines?: boolean;
|
||||
}
|
||||
|
||||
interface ScaleTitleOptions {
|
||||
display?: boolean;
|
||||
labelString?: string;
|
||||
fontColor?: ChartColor;
|
||||
fontFamily?: string;
|
||||
fontSize?: number;
|
||||
fontStyle?: string;
|
||||
}
|
||||
|
||||
interface TickOptions {
|
||||
autoSkip?: boolean;
|
||||
autoSkipPadding?: number;
|
||||
callback?(value: any, index: any, values: any): string|number;
|
||||
display?: boolean;
|
||||
fontColor?: ChartColor;
|
||||
fontFamily?: string;
|
||||
fontSize?: number;
|
||||
fontStyle?: string;
|
||||
labelOffset?: number;
|
||||
maxRotation?: number;
|
||||
minRotation?: number;
|
||||
mirror?: boolean;
|
||||
padding?: number;
|
||||
reverse?: boolean;
|
||||
min?: any;
|
||||
max?: any;
|
||||
}
|
||||
interface AngleLineOptions {
|
||||
display?: boolean;
|
||||
color?: ChartColor;
|
||||
lineWidth?: number;
|
||||
}
|
||||
|
||||
interface PointLabelOptions {
|
||||
callback?(arg: any): any;
|
||||
fontColor?: ChartColor;
|
||||
fontFamily?: string;
|
||||
fontSize?: number;
|
||||
fontStyle?: string;
|
||||
}
|
||||
|
||||
interface TickOptions {
|
||||
backdropColor?: ChartColor;
|
||||
backdropPaddingX?: number;
|
||||
backdropPaddingY?: number;
|
||||
maxTicksLimit?: number;
|
||||
showLabelBackdrop?: boolean;
|
||||
}
|
||||
interface LinearTickOptions extends TickOptions {
|
||||
beginAtZero?: boolean;
|
||||
min?: number;
|
||||
max?: number;
|
||||
maxTicksLimit?: number;
|
||||
stepSize?: number;
|
||||
suggestedMin?: number;
|
||||
suggestedMax?: number;
|
||||
}
|
||||
|
||||
interface LogarithmicTickOptions extends TickOptions {
|
||||
min?: number;
|
||||
max?: number;
|
||||
}
|
||||
|
||||
type ChartColor = string | CanvasGradient | CanvasPattern | string[];
|
||||
|
||||
interface ChartDataSets {
|
||||
cubicInterpolationMode?: 'default' | 'monotone';
|
||||
backgroundColor?: ChartColor | ChartColor[];
|
||||
borderWidth?: number | number[];
|
||||
borderColor?: ChartColor | ChartColor[];
|
||||
borderCapStyle?: string;
|
||||
borderDash?: number[];
|
||||
borderDashOffset?: number;
|
||||
borderJoinStyle?: string;
|
||||
borderSkipped?: PositionType;
|
||||
data?: number[] | ChartPoint[];
|
||||
fill?: boolean | number | string;
|
||||
hoverBackgroundColor?: string | string[];
|
||||
hoverBorderColor?: string | string[];
|
||||
hoverBorderWidth?: number | number[];
|
||||
label?: string;
|
||||
lineTension?: number;
|
||||
steppedLine?: 'before' | 'after' | boolean;
|
||||
pointBorderColor?: ChartColor | ChartColor[];
|
||||
pointBackgroundColor?: ChartColor | ChartColor[];
|
||||
pointBorderWidth?: number | number[];
|
||||
pointRadius?: number | number[];
|
||||
pointHoverRadius?: number | number[];
|
||||
pointHitRadius?: number | number[];
|
||||
pointHoverBackgroundColor?: ChartColor | ChartColor[];
|
||||
pointHoverBorderColor?: ChartColor | ChartColor[];
|
||||
pointHoverBorderWidth?: number | number[];
|
||||
pointStyle?: PointStyle | HTMLImageElement | Array<PointStyle | HTMLImageElement>;
|
||||
xAxisID?: string;
|
||||
yAxisID?: string;
|
||||
type?: string;
|
||||
hidden?: boolean;
|
||||
hideInLegendAndTooltip?: boolean;
|
||||
showLine?: boolean;
|
||||
stack?: string;
|
||||
spanGaps?: boolean;
|
||||
}
|
||||
|
||||
interface ChartScales {
|
||||
type?: ScaleType | string;
|
||||
display?: boolean;
|
||||
position?: PositionType | string;
|
||||
gridLines?: GridLineOptions;
|
||||
scaleLabel?: ScaleTitleOptions;
|
||||
ticks?: TickOptions;
|
||||
xAxes?: ChartXAxe[];
|
||||
yAxes?: ChartYAxe[];
|
||||
}
|
||||
|
||||
interface CommonAxe {
|
||||
type?: ScaleType | string;
|
||||
display?: boolean;
|
||||
id?: string;
|
||||
stacked?: boolean;
|
||||
position?: string;
|
||||
ticks?: TickOptions;
|
||||
gridLines?: GridLineOptions;
|
||||
barThickness?: number;
|
||||
scaleLabel?: ScaleTitleOptions;
|
||||
beforeUpdate?(scale?: any): void;
|
||||
beforeSetDimension?(scale?: any): void;
|
||||
beforeDataLimits?(scale?: any): void;
|
||||
beforeBuildTicks?(scale?: any): void;
|
||||
beforeTickToLabelConversion?(scale?: any): void;
|
||||
beforeCalculateTickRotation?(scale?: any): void;
|
||||
beforeFit?(scale?: any): void;
|
||||
afterUpdate?(scale?: any): void;
|
||||
afterSetDimension?(scale?: any): void;
|
||||
afterDataLimits?(scale?: any): void;
|
||||
afterBuildTicks?(scale?: any): void;
|
||||
afterTickToLabelConversion?(scale?: any): void;
|
||||
afterCalculateTickRotation?(scale?: any): void;
|
||||
afterFit?(scale?: any): void;
|
||||
}
|
||||
|
||||
interface ChartXAxe extends CommonAxe {
|
||||
categoryPercentage?: number;
|
||||
barPercentage?: number;
|
||||
time?: TimeScale;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line no-empty-interface
|
||||
interface ChartYAxe extends CommonAxe {
|
||||
}
|
||||
|
||||
interface LinearScale extends ChartScales {
|
||||
ticks?: LinearTickOptions;
|
||||
}
|
||||
|
||||
interface LogarithmicScale extends ChartScales {
|
||||
ticks?: LogarithmicTickOptions;
|
||||
}
|
||||
|
||||
interface TimeDisplayFormat {
|
||||
millisecond?: string;
|
||||
second?: string;
|
||||
minute?: string;
|
||||
hour?: string;
|
||||
day?: string;
|
||||
week?: string;
|
||||
month?: string;
|
||||
quarter?: string;
|
||||
year?: string;
|
||||
}
|
||||
|
||||
interface TimeScale extends ChartScales {
|
||||
displayFormats?: TimeDisplayFormat;
|
||||
isoWeekday?: boolean;
|
||||
max?: string;
|
||||
min?: string;
|
||||
parser?: string | ((arg: any) => any);
|
||||
round?: TimeUnit;
|
||||
tooltipFormat?: string;
|
||||
unit?: TimeUnit;
|
||||
unitStepSize?: number;
|
||||
stepSize?: number;
|
||||
minUnit?: TimeUnit;
|
||||
}
|
||||
|
||||
interface RadialLinearScale {
|
||||
lineArc?: boolean;
|
||||
angleLines?: AngleLineOptions;
|
||||
pointLabels?: PointLabelOptions;
|
||||
ticks?: TickOptions;
|
||||
}
|
||||
|
||||
interface Point {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
}
|
||||
|
||||
export = Chart;
|
||||
export as namespace Chart;
|
199
assets/swirl/typings/echarts.d.ts
vendored
Normal file
199
assets/swirl/typings/echarts.d.ts
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
// Type definitions for echarts
|
||||
// Project: http://echarts.baidu.com/
|
||||
// Definitions by: Xie Jingyang <https://github.com/xieisabug>, AntiMoron <https://github.com/AntiMoron>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
// TypeScript Version: 2.3
|
||||
|
||||
declare namespace echarts {
|
||||
function init(dom: HTMLDivElement | HTMLCanvasElement, theme?: Object | string, opts?: {
|
||||
devicePixelRatio?: number
|
||||
renderer?: string
|
||||
}): ECharts;
|
||||
|
||||
const graphic: Graphic;
|
||||
|
||||
interface Graphic {
|
||||
clipPointsByRect(points: number[][], rect: ERectangle): number[][];
|
||||
clipRectByRect(targetRect: ERectangle, rect: ERectangle): ERectangle;
|
||||
LinearGradient: { new(x: number, y: number, x2: number, y2: number, colorStops: Array<Object>, globalCoord?: boolean): LinearGradient }
|
||||
}
|
||||
|
||||
function connect(group: string | Array<string>): void;
|
||||
|
||||
function disConnect(group: string): void;
|
||||
|
||||
function dispose(target: ECharts | HTMLDivElement | HTMLCanvasElement): void;
|
||||
|
||||
function getInstanceByDom(target: HTMLDivElement | HTMLCanvasElement): ECharts;
|
||||
|
||||
function registerMap(mapName: string, geoJson: Object, specialAreas?: Object): void;
|
||||
|
||||
function registerTheme(themeName: string, theme: Object): void;
|
||||
|
||||
interface MapObj {
|
||||
/** geoJson data for map */
|
||||
geoJson: object,
|
||||
/** special areas fro map */
|
||||
specialAreas: object
|
||||
}
|
||||
|
||||
function getMap(mapName: string): MapObj;
|
||||
|
||||
interface LinearGradient {
|
||||
colorStops: Array<Object>;
|
||||
global: boolean;
|
||||
type: string;
|
||||
x: number
|
||||
x2: number
|
||||
y: number
|
||||
y2: number
|
||||
}
|
||||
|
||||
interface ECharts {
|
||||
group: string
|
||||
|
||||
setOption(option: EChartOption, notMerge?: boolean, notRefreshImmediately?: boolean): void
|
||||
|
||||
getWidth(): number
|
||||
|
||||
getHeight(): number
|
||||
|
||||
getDom(): HTMLCanvasElement | HTMLDivElement
|
||||
|
||||
getOption(): Object
|
||||
|
||||
resize(): void
|
||||
|
||||
dispatchAction(payload: Object): void
|
||||
|
||||
on(eventName: string, handler: Function, context?: Object): void
|
||||
|
||||
off(eventName: string, handler?: Function): void
|
||||
|
||||
showLoading(type?: string, opts?: Object): void
|
||||
|
||||
hideLoading(): void
|
||||
|
||||
getDataURL(opts: {
|
||||
/** 导出的格式,可选 png, jpeg */
|
||||
type?: string,
|
||||
/** 导出的图片分辨率比例,默认为 1。*/
|
||||
pixelRatio?: number,
|
||||
/** 导出的图片背景色,默认使用 option 里的 backgroundColor */
|
||||
backgroundColor?: string
|
||||
}): string
|
||||
|
||||
getConnectedDataURL(opts: {
|
||||
/** 导出的格式,可选 png, jpeg */
|
||||
type: string,
|
||||
/** 导出的图片分辨率比例,默认为 1。 */
|
||||
pixelRatio: number,
|
||||
/** 导出的图片背景色,默认使用 option 里的 backgroundColor */
|
||||
backgroundColor: string
|
||||
}): string
|
||||
|
||||
clear(): void
|
||||
|
||||
isDisposed(): boolean
|
||||
|
||||
dispose(): void
|
||||
|
||||
/** 转换逻辑点到像素 */
|
||||
convertToPixel(finder: ConvertFinder | string, value: string | Array<any>): string | Array<any>
|
||||
|
||||
convertFromPixel(finder: ConvertFinder | string, value: Array<any> | string): Array<any> | string
|
||||
|
||||
containPixel(finder: ConvertFinder | string,
|
||||
/** 要被判断的点,为像素坐标值,以 echarts 实例的 dom 节点的左上角为坐标 [0, 0] 点。*/
|
||||
value: any[]): boolean
|
||||
}
|
||||
|
||||
interface ConvertFinder {
|
||||
seriesIndex?: number,
|
||||
seriesId?: string,
|
||||
seriesName?: string,
|
||||
geoIndex?: number,
|
||||
geoId?: string,
|
||||
geoName?: string,
|
||||
xAxisIndex?: number,
|
||||
xAxisId?: string,
|
||||
xAxisName?: string,
|
||||
yAxisIndex?: number,
|
||||
yAxisId?: string,
|
||||
yAxisName?: string,
|
||||
gridIndex?: number,
|
||||
gridId?: string
|
||||
gridName?: string
|
||||
}
|
||||
|
||||
interface ERectangle {
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number
|
||||
}
|
||||
|
||||
interface EChartOption {
|
||||
title?: EChartTitleOption
|
||||
legend?: Object,
|
||||
grid?: Object,
|
||||
xAxis?: Object,
|
||||
yAxis?: Object,
|
||||
polar?: Object,
|
||||
radiusAxis?: Object,
|
||||
angleAxis?: Object,
|
||||
radar?: Object,
|
||||
dataZoom?: Array<Object>,
|
||||
visualMap?: Array<Object>,
|
||||
tooltip?: Object,
|
||||
toolbox?: Object,
|
||||
geo?: Object,
|
||||
parallel?: Object,
|
||||
parallelAxis?: Object,
|
||||
timeline?: Object,
|
||||
series?: Array<Object>,
|
||||
color?: Array<Object>,
|
||||
backgroundColor?: string,
|
||||
textStyle?: Object,
|
||||
animation?: boolean,
|
||||
animationDuration?: number,
|
||||
animationEasing?: string,
|
||||
animationDurationUpdate?: number,
|
||||
animationEasingUpdate?: string
|
||||
}
|
||||
|
||||
interface EChartTitleOption {
|
||||
show?: boolean;
|
||||
text?: string;
|
||||
link?: string,
|
||||
target?: string,
|
||||
textStyle?: Object,
|
||||
subtext?: string,
|
||||
sublink?: string,
|
||||
subtarget?: string,
|
||||
subtextStyle?: Object,
|
||||
padding?: number,
|
||||
itemGap?: number,
|
||||
zlevel?: number,
|
||||
z?: number,
|
||||
left?: string,
|
||||
top?: string,
|
||||
right?: string,
|
||||
bottom?: string,
|
||||
backgroundColor?: string,
|
||||
borderColor?: string,
|
||||
borderWidth?: number,
|
||||
shadowBlur?: number,
|
||||
shadowColor?: number,
|
||||
shadowOffsetX?: number,
|
||||
shadowOffsetY?: number,
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'echarts' {
|
||||
export = echarts;
|
||||
}
|
||||
|
||||
declare module 'echarts/lib/echarts' {
|
||||
export = echarts;
|
||||
}
|
27
biz/chart.go
27
biz/chart.go
@ -144,9 +144,9 @@ func (b *chartBiz) GetDashboardCharts(dashboard *model.ChartDashboard) (charts [
|
||||
if c.Height > 0 {
|
||||
chart.Height = c.Height
|
||||
}
|
||||
if len(c.Colors) > 0 {
|
||||
chart.Colors = c.Colors
|
||||
}
|
||||
//if len(c.Colors) > 0 {
|
||||
// chart.Colors = c.Colors
|
||||
//}
|
||||
charts = append(charts, chart)
|
||||
}
|
||||
}
|
||||
@ -172,20 +172,29 @@ func (b *chartBiz) FetchDatas(key string, names []string, period time.Duration)
|
||||
|
||||
switch chart.Type {
|
||||
case "line", "bar":
|
||||
m, err := Metric.GetMatrix(query, chart.Label, start, end)
|
||||
d, err := Metric.GetMatrix(query, chart.Legend, start, end)
|
||||
if err != nil {
|
||||
log.Get("metric").Error(err, query)
|
||||
} else {
|
||||
datas[chart.Name] = m
|
||||
datas[chart.Name] = d
|
||||
}
|
||||
case "pie", "table":
|
||||
m, err := Metric.GetVector(query, chart.Label, end)
|
||||
case "pie":
|
||||
d, err := Metric.GetVector(query, chart.Legend, end)
|
||||
if err != nil {
|
||||
log.Get("metric").Error(err, query)
|
||||
} else {
|
||||
datas[chart.Name] = m
|
||||
datas[chart.Name] = d
|
||||
}
|
||||
case "gauge":
|
||||
d, err := Metric.GetScalar(query, end)
|
||||
if err != nil {
|
||||
log.Get("metric").Error(err, query)
|
||||
} else {
|
||||
datas[chart.Name] = &model.ChartValue{
|
||||
//Name: "",
|
||||
Value: d,
|
||||
}
|
||||
}
|
||||
case "value":
|
||||
}
|
||||
}
|
||||
return datas, nil
|
||||
|
@ -57,7 +57,7 @@ type metricBiz struct {
|
||||
// return
|
||||
// }
|
||||
|
||||
func (b *metricBiz) GetMatrix(query, label string, start, end time.Time) (lines []model.ChartLine, err error) {
|
||||
func (b *metricBiz) GetMatrix(query, legend string, start, end time.Time) (data *model.ChartMatrixData, err error) {
|
||||
api, err := b.getAPI()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -73,9 +73,11 @@ func (b *metricBiz) GetMatrix(query, label string, start, end time.Time) (lines
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data = &model.ChartMatrixData{}
|
||||
matrix := value.(pmodel.Matrix)
|
||||
for _, stream := range matrix {
|
||||
line := model.ChartLine{Label: b.formatLabel(label, stream.Metric)}
|
||||
data.Legend = append(data.Legend, b.formatLabel(legend, stream.Metric))
|
||||
line := model.ChartLine{Name: b.formatLabel(legend, stream.Metric)}
|
||||
for _, v := range stream.Values {
|
||||
p := model.ChartPoint{
|
||||
X: int64(v.Timestamp),
|
||||
@ -83,7 +85,7 @@ func (b *metricBiz) GetMatrix(query, label string, start, end time.Time) (lines
|
||||
}
|
||||
line.Data = append(line.Data, p)
|
||||
}
|
||||
lines = append(lines, line)
|
||||
data.Series = append(data.Series, line)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -99,11 +101,16 @@ func (b *metricBiz) GetScalar(query string, t time.Time) (v float64, err error)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
scalar := value.(*pmodel.Scalar)
|
||||
return float64(scalar.Value), nil
|
||||
//scalar := value.(*pmodel.Scalar)
|
||||
vector := value.(pmodel.Vector)
|
||||
if len(vector) > 0 {
|
||||
sample := vector[0]
|
||||
return float64(sample.Value), nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (b *metricBiz) GetVector(query, label string, t time.Time) (cv model.ChartVector, err error) {
|
||||
func (b *metricBiz) GetVector(query, label string, t time.Time) (data *model.ChartVectorData, err error) {
|
||||
var api papi.API
|
||||
api, err = b.getAPI()
|
||||
if err != nil {
|
||||
@ -116,12 +123,15 @@ func (b *metricBiz) GetVector(query, label string, t time.Time) (cv model.ChartV
|
||||
return
|
||||
}
|
||||
|
||||
data = &model.ChartVectorData{}
|
||||
vector := value.(pmodel.Vector)
|
||||
for _, sample := range vector {
|
||||
cv.Data = append(cv.Data, float64(sample.Value))
|
||||
if label != "" {
|
||||
cv.Labels = append(cv.Labels, b.formatLabel(label, sample.Metric))
|
||||
cv := model.ChartValue{
|
||||
Name: b.formatLabel(label, sample.Metric),
|
||||
Value: float64(sample.Value),
|
||||
}
|
||||
data.Data = append(data.Data, cv)
|
||||
data.Legend = append(data.Legend, cv.Name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -49,7 +49,8 @@ field.width: Width
|
||||
field.height: Height
|
||||
field.unit: Unit
|
||||
field.title: Title
|
||||
field.label: Label
|
||||
field.legend: Legend
|
||||
field.desc: Description
|
||||
field.dashboard: Dashboard
|
||||
|
||||
# menu
|
||||
|
@ -49,7 +49,8 @@ field.width: 宽度
|
||||
field.height: 高度
|
||||
field.unit: 单位
|
||||
field.title: 标题
|
||||
field.label: 标签
|
||||
field.legend: 图例
|
||||
field.desc: 描述
|
||||
field.dashboard: 仪表盘
|
||||
|
||||
# menu
|
||||
|
@ -69,7 +69,7 @@ func chartQuery(ctx web.Context) error {
|
||||
func chartNew(ctx web.Context) error {
|
||||
m := newModel(ctx).Set("Chart", &model.Chart{
|
||||
Width: 12,
|
||||
Height: 150,
|
||||
Height: 200,
|
||||
Type: "line",
|
||||
Dashboard: "service",
|
||||
})
|
||||
@ -115,7 +115,7 @@ func chartDelete(ctx web.Context) error {
|
||||
}
|
||||
|
||||
func chartData(ctx web.Context) error {
|
||||
period := cast.ToDuration(ctx.Q("time"), time.Hour)
|
||||
period := time.Duration(cast.ToInt64(ctx.Q("period"), 60)) * time.Minute
|
||||
if v := ctx.Q("charts"); v != "" {
|
||||
names := strings.Split(v, ",")
|
||||
key := ctx.Q("key")
|
||||
|
@ -1,7 +1,10 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cuigh/auxo/data"
|
||||
"github.com/cuigh/auxo/util/cast"
|
||||
)
|
||||
|
||||
// Chart represents a dashboard chart.
|
||||
@ -9,7 +12,7 @@ type Chart struct {
|
||||
Name string `json:"name" bson:"_id" valid:"required"` // unique, the name of build-in charts has '$' prefix.
|
||||
Title string `json:"title" valid:"required"`
|
||||
Description string `json:"desc"`
|
||||
Label string `json:"label"` // ${name} - ${instance}
|
||||
Legend string `json:"legend"` // ${name} - ${instance}
|
||||
Query string `json:"query" valid:"required"`
|
||||
Kind string `json:"kind"` // builtin/custom
|
||||
Dashboard string `json:"dashboard"` // home/service/task...
|
||||
@ -17,38 +20,38 @@ type Chart struct {
|
||||
Unit string `json:"unit"` // bytes/milliseconds/percent:100...
|
||||
Width int32 `json:"width"` // 1-12(12 columns total)
|
||||
Height int32 `json:"height"` // default 50
|
||||
Colors []string `json:"colors"`
|
||||
Options data.Map `json:"options"`
|
||||
//Colors []string `json:"colors"`
|
||||
}
|
||||
|
||||
func NewChart(dashboard, name, title, label, query, unit string) *Chart {
|
||||
func NewChart(dashboard, name, title, legend, query, unit string) *Chart {
|
||||
return &Chart{
|
||||
Name: name,
|
||||
Title: title,
|
||||
Description: title,
|
||||
Label: label,
|
||||
Legend: legend,
|
||||
Query: query,
|
||||
Dashboard: dashboard,
|
||||
Type: "line",
|
||||
Unit: unit,
|
||||
Width: 12,
|
||||
Height: 150,
|
||||
Height: 200,
|
||||
}
|
||||
}
|
||||
|
||||
type ChartItem struct {
|
||||
Name string `json:"name"`
|
||||
Width int32 `json:"width"`
|
||||
Height int32 `json:"height"`
|
||||
Colors []string `json:"colors"`
|
||||
type ChartOption struct {
|
||||
Name string `json:"name"`
|
||||
Width int32 `json:"width"`
|
||||
Height int32 `json:"height"`
|
||||
//Colors []string `json:"colors"`
|
||||
}
|
||||
|
||||
type ChartDashboard struct {
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
Period int32 `json:"period"` // minutes
|
||||
RefreshInterval int32 `json:"refresh_interval"` // seconds, 0 means disabled.
|
||||
Charts []ChartItem `json:"charts"`
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
Period int32 `json:"period"` // minutes
|
||||
RefreshInterval int32 `json:"refresh_interval"` // seconds, 0 means disabled.
|
||||
Charts []ChartOption `json:"charts"`
|
||||
}
|
||||
|
||||
func (cd *ChartDashboard) ID() string {
|
||||
@ -63,33 +66,26 @@ type ChartPoint struct {
|
||||
Y float64 `json:"y"`
|
||||
}
|
||||
|
||||
func (p *ChartPoint) MarshalJSON() ([]byte, error) {
|
||||
return cast.StringToBytes(fmt.Sprintf("[%v,%v]", p.X, p.Y)), nil
|
||||
}
|
||||
|
||||
type ChartLine struct {
|
||||
Label string `json:"label"`
|
||||
Data []ChartPoint `json:"data"`
|
||||
Name string `json:"name"`
|
||||
Data []ChartPoint `json:"data"`
|
||||
}
|
||||
|
||||
type ChartMatrixData struct {
|
||||
Legend []string `json:"legend"`
|
||||
Series []ChartLine `json:"series"`
|
||||
}
|
||||
|
||||
type ChartValue struct {
|
||||
Label string `json:"label"`
|
||||
Data float64 `json:"data"`
|
||||
Name string `json:"name"`
|
||||
Value float64 `json:"value"`
|
||||
}
|
||||
|
||||
type ChartVector struct {
|
||||
Data []float64 `json:"data"`
|
||||
Labels []string `json:"labels"`
|
||||
}
|
||||
|
||||
type ChartInfo struct {
|
||||
Name string `json:"name"`
|
||||
Title string `json:"title"`
|
||||
Label string `json:"label"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
func NewChartInfo(name, title, label, query string) ChartInfo {
|
||||
return ChartInfo{
|
||||
Name: name,
|
||||
Title: title,
|
||||
Label: label,
|
||||
Query: query,
|
||||
}
|
||||
type ChartVectorData struct {
|
||||
Legend []string `json:"legend"`
|
||||
Data []ChartValue `json:"data"`
|
||||
}
|
||||
|
@ -168,11 +168,11 @@ func cpuChecker(service string, low, high float64) (scaleType, float64) {
|
||||
return scaleNone, 0
|
||||
}
|
||||
|
||||
value := vector.Data[0]
|
||||
if value <= low {
|
||||
return scaleDown, value
|
||||
} else if value >= high {
|
||||
return scaleUp, value
|
||||
cv := vector.Data[0]
|
||||
if cv.Value <= low {
|
||||
return scaleDown, cv.Value
|
||||
} else if cv.Value >= high {
|
||||
return scaleUp, cv.Value
|
||||
}
|
||||
return scaleNone, 0
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
{{ extends "_layouts/default" }}
|
||||
|
||||
{{ block script() }}
|
||||
<script src="/assets/chart/chart.bundle.min.js?v=2.7.2"></script>
|
||||
<script>var charts = {{ writeJson(.Charts) }};</script>
|
||||
<script src="/assets/echarts/echarts.min.js?v=4.0.4"></script>
|
||||
<script>$(() => new Swirl.IndexPage())</script>
|
||||
{{ end }}
|
||||
|
||||
@ -85,35 +86,6 @@
|
||||
</div>
|
||||
</nav>
|
||||
<div id="div-charts" class="columns is-multiline">
|
||||
{{ range .Charts }}
|
||||
<div class="column is-{{ .Width }}" data-chart-name="{{ .Name }}" data-chart-type="{{ .Type }}" data-chart-unit="{{ .Unit }}" data-chart-width="{{ .Width }}" data-chart-height="{{ .Height }}">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">{{ .Title }}</p>
|
||||
<a data-action="remove-chart" class="card-header-icon" aria-label="remove chart">
|
||||
<span class="icon">
|
||||
<i class="fas fa-times has-text-danger" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
{*<a data-action="remove-chart" class="card-header-icon is-paddingless" aria-label="remove chart">*}
|
||||
{*<span class="icon">*}
|
||||
{*<i class="fas fa-times has-text-danger" aria-hidden="true"></i>*}
|
||||
{*</span>*}
|
||||
{*</a>*}
|
||||
{*<a data-action="edit-options" class="card-header-icon" aria-label="edit options">*}
|
||||
{*<span class="icon">*}
|
||||
{*<i class="fas fa-ellipsis-h has-text-info" aria-hidden="true"></i>*}
|
||||
{*</span>*}
|
||||
{*</a>*}
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div style="height: {{ .Height }}px">
|
||||
<canvas id="canvas_{{ .Name }}"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
@ -3,7 +3,8 @@
|
||||
{{ import "../_modules/form" }}
|
||||
|
||||
{{ block script() }}
|
||||
<script src="/assets/chart/chart.bundle.min.js?v=2.7.2"></script>
|
||||
<script>var charts = {{ writeJson(.Charts) }};</script>
|
||||
<script src="/assets/echarts/echarts.min.js?v=4.0.4"></script>
|
||||
<script>$(() => new Swirl.Service.StatsPage())</script>
|
||||
{{ end }}
|
||||
|
||||
@ -53,12 +54,12 @@
|
||||
<p class="control">
|
||||
<div class="select">
|
||||
<select id="cb-time" name="time">
|
||||
{{ yield option(value="30m", label="Last 30 minutes", selected=.Time) }}
|
||||
{{ yield option(value="1h", label="Last 1 hour", selected=.Time) }}
|
||||
{{ yield option(value="3h", label="Last 3 hours", selected=.Time) }}
|
||||
{{ yield option(value="6h", label="Last 6 hours", selected=.Time) }}
|
||||
{{ yield option(value="12h", label="Last 12 hours", selected=.Time) }}
|
||||
{{ yield option(value="24h", label="Last 24 hours", selected=.Time) }}
|
||||
{{ yield option(value="30", label="Last 30 minutes", selected=.Time) }}
|
||||
{{ yield option(value="60", label="Last 1 hour", selected=.Time) }}
|
||||
{{ yield option(value="180", label="Last 3 hours", selected=.Time) }}
|
||||
{{ yield option(value="360", label="Last 6 hours", selected=.Time) }}
|
||||
{{ yield option(value="720", label="Last 12 hours", selected=.Time) }}
|
||||
{{ yield option(value="1440", label="Last 24 hours", selected=.Time) }}
|
||||
</select>
|
||||
</div>
|
||||
</p>
|
||||
@ -82,30 +83,6 @@
|
||||
</div>
|
||||
</nav>
|
||||
<div id="div-charts" class="columns is-multiline">
|
||||
{{ range .Charts }}
|
||||
<div class="column is-{{ .Width }}" data-chart-name="{{ .Name }}" data-chart-type="{{ .Type }}" data-chart-unit="{{ .Unit }}" data-chart-width="{{ .Width }}" data-chart-height="{{ .Height }}">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">{{ .Title }}</p>
|
||||
<a data-action="remove-chart" class="card-header-icon" aria-label="remove chart">
|
||||
<span class="icon">
|
||||
<i class="fas fa-times has-text-danger" aria-hidden="true"></i>
|
||||
</span>
|
||||
</a>
|
||||
{*<a data-action="edit-options" class="card-header-icon" aria-label="edit options">*}
|
||||
{*<span class="icon">*}
|
||||
{*<i class="fas fa-ellipsis-h has-text-info" aria-hidden="true"></i>*}
|
||||
{*</span>*}
|
||||
{*</a>*}
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div style="height: {{ .Height }}px">
|
||||
<canvas id="canvas_{{ .Name }}"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="notification is-info">
|
||||
|
@ -22,9 +22,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">{{ i18n("field.label") }}</label>
|
||||
<label class="label">{{ i18n("field.legend") }}</label>
|
||||
<div class="control">
|
||||
<input name="label" value="{{ .Chart.Label }}" class="input" placeholder="Label for dataset, e.g. ${name}">
|
||||
<input name="legend" value="{{ .Chart.Legend }}" class="input" placeholder="Legend expression for dataset, e.g. ${name}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -53,7 +53,7 @@
|
||||
<div class="field">
|
||||
<label class="label">{{ i18n("field.height") }}</label>
|
||||
<div class="control">
|
||||
<input name="height" value="{{ .Chart.Height }}" class="input" placeholder="Height" data-type="integer" data-v-rule="native" required>
|
||||
<input name="height" value="{{ .Chart.Height }}" class="input" placeholder="Height in pixel" data-type="integer" data-v-rule="native" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
@ -89,7 +89,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Description</label>
|
||||
<label class="label">{{ i18n("field.desc") }}</label>
|
||||
<div class="control">
|
||||
<input name="desc" value="{{ .Chart.Description }}" class="input" placeholder="Chart description">
|
||||
</div>
|
||||
@ -106,8 +106,8 @@
|
||||
{{ yield radio(name="type", value="line", label="Line", checked=.Chart.Type) }}
|
||||
{{ yield radio(name="type", value="bar", label="Bar", checked=.Chart.Type) }}
|
||||
{{ yield radio(name="type", value="pie", label="Pie", checked=.Chart.Type) }}
|
||||
{{ yield radio(name="type", value="gauge", label="Gauge", checked=.Chart.Type) }}
|
||||
{*{{ yield radio(name="type", value="table", label="Table", checked=.Chart.Type) }}*}
|
||||
{{ yield radio(name="type", value="value", label="Value", checked=.Chart.Type) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
@ -116,7 +116,7 @@
|
||||
{{ yield radio(name="dashboard", value="", label="Any", checked=.Chart.Dashboard) }}
|
||||
{{ yield radio(name="dashboard", value="home", label="Home", checked=.Chart.Dashboard) }}
|
||||
{{ yield radio(name="dashboard", value="service", label="Service", checked=.Chart.Dashboard) }}
|
||||
{{ yield radio(name="dashboard", value="task", label="Task", checked=.Chart.Dashboard) }}
|
||||
{*{{ yield radio(name="dashboard", value="task", label="Task", checked=.Chart.Dashboard) }}*}
|
||||
</div>
|
||||
</div>
|
||||
{{ yield form_submit(url="/system/chart/") }}
|
||||
|
Loading…
Reference in New Issue
Block a user