dc.js - 如何计算总销售额并使显示的总数动态化?
问题描述
我需要向卖家和城市展示我的数据的总销售额。但是,总计中显示的值必须是动态的。
PS My table 允许用户选择行,这是因为值是根据所做的每个选择呈现的。(桌子之间的这种活力已经很好地发挥了作用)。
Total 的初始状态是用所有卖家和城市的总和呈现的,下面我展示了带有各自总计的表格,但是,如图所示,这些值是静态的而不是动态的。
我需要它是 diahanic,因为如果我选择例如卖方João Luis
和Miguel
,则要显示在总数中的值必须是这两个卖方的总和。
前段时间我问了一个关于如何在数据表中添加行的问题 - DC.js,一位朋友 (@Gordon) 在 DC.js 和 Crossfilter 的疑问中帮助了我很多,Crossfilter 告诉我使用crossfilter.add()
. 不幸的是,我不能按照建议的方式去做,所以我又来了几乎同样的问题(对不起我的愚蠢)。
var composite = dc.compositeChart('#composite');
var vendedorTable = dc.dataTable("#vendedores");
var citiesTable = dc.dataTable("#cities");
function remove_empty_bins(source_group) {
return {
top: function(N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 ||
d.value.totalHomologo > 1e-3;
}).slice(0, N);
}
};
}
var adjustX = 10,
adjustY = 40;
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(d =>
Object.assign(d, {
mes: d.Month,
atual: d.Vendas_Ano,
passado: d.Vendas_Ant
})
);
var cf = crossfilter(data);
vendedorDim = cf.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = cf.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
var dim = cf.dimension(dc.pluck('mes')),
grp1 = dim.group().reduceSum(dc.pluck('atual')),
grp2 = dim.group().reduceSum(dc.pluck('passado'));
var minMonth = dim.bottom(1)[0].mes;
var maxMonth = dim.top(1)[0].mes;
var all = cf.groupAll();
dc.dataCount(".dc-data-count")
.dimension(cf)
.group(all);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
// Fake Dimension
rank = function(p) {
return ""
};
// Chart by months
composite
.width(window.innerWidth - adjustX)
.height(300)
.x(d3.scaleLinear().domain([1, 12]))
.yAxisLabel("")
.xAxisLabel("Month")
.legend(dc.legend().x(500).y(0).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.lineChart(composite)
.dimension(dim)
.colors('steelblue')
.group(grp1, "Currently Year"),
dc.lineChart(composite)
.dimension(dim)
.colors('darkorange')
.group(grp2, "Last Year")
])
.brushOn(true);
composite.brush().extent([-0.5, data.length + 1.5])
composite.extendBrush = function(brushSelection) {
if (brushSelection) {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
const point = Math.round((brushSelection[0] + brushSelection[1]) / 2);
return [
point - 0.5,
point + 0.5
];
}
};
// Sales Table
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Cities Table
citiesTable
.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.controlsUseVisibility(true)
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Sales click events
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
// Cities click events
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
vendedorTable.filter(null);
vendedorDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
// reset functions
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
/****************************************************************************/
// Functions to handle responsive
apply_resizing(composite, adjustX, adjustY, function(composite) {
composite.legend().x(window.innerWidth - 200);
});
var find_query = function() {
var _map = window.location.search.substr(1).split('&').map(function(a) {
return a.split('=');
}).reduce(function(p, v) {
if (v.length > 1)
p[v[0]] = decodeURIComponent(v[1].replace(/\+/g, " "));
else
p[v[0]] = true;
return p;
}, {});
return function(field) {
return _map[field] || null;
};
}();
var resizeMode = find_query('resize') || 'widhei';
function apply_resizing(composite, adjustX, adjustY, onresize) {
if (resizeMode === 'viewbox') {
composite
.width(300)
.height(200)
.useViewBoxResizing(true);
d3.select(composite.anchor()).classed('fullsize', false);
} else {
adjustX = adjustX || 0;
adjustY = adjustY || adjustX || 0;
composite
.width(window.innerWidth - adjustX)
.height(300);
window.onresize = function() {
if (onresize) {
onresize(composite);
}
composite
.width(window.innerWidth - adjustX)
.height(300);
if (composite.rescale) {
composite.rescale();
}
composite.redraw();
};
}
}
});
#composite {
padding: 10px;
}
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
.brush .custom-brush-handle {
display: none;
}
<head>
<!-- favicon -->
<link rel="shortcut icon" href="https://img.icons8.com/nolan/64/puzzle.png">
<!-- bootstrap.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- bootstrap-theme.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- dc.css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<!-- jquery.js -->
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<!-- bootstrap.js -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- d3.v5.js -->
<script src="https://d3js.org/d3.v5.js"></script>
<!-- crossfilter.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<!-- dc.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>Chart</title>
</head>
<body>
<div id="composite"></div>
<div class="container-fluid">
<div class="row content">
<div class="col-md-12">
<div class="col-md-6">
<table class="table" id="totaisVendedores">
<thead>
<tr>
<th>Total Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="totaisCities">
<thead>
<tr>
<th>Total City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>
解决方案
对不起,我完全误解了你的最后一个问题。你很高兴接受这个答案,但你真的不必那样做。:)
为了添加总计,您可以在表格中添加一个tfoot部分
<tfoot>
<tr>
<th>TOTAL</th>
<th id="total-current-year"></th>
<th id="total-last-year"></th>
</tr>
</tfoot>
并使用NumberDisplay 小部件驱动它。
const totalCYDisplay = dc.numberDisplay('#total-current-year')
.group(totalAll)
.valueAccessor(d => d.totalAno)
默认格式是但接受任何.2s
d3 -format instance。numberDisplay
如果你不喜欢动画,你可以设置.transitionDuration(0)
var composite = dc.compositeChart('#composite');
var vendedorTable = dc.dataTable("#vendedores");
var citiesTable = dc.dataTable("#cities");
function remove_empty_bins(source_group) {
return {
top: function(N) {
return source_group.all().filter(function(d) {
return d.value.totalAno > 1e-3 ||
d.value.totalHomologo > 1e-3;
}).slice(0, N);
}
};
}
var adjustX = 10,
adjustY = 40;
var url = 'https://gist.githubusercontent.com/bernalvinicius/3cece295bc37de1697e7f83418e7fcc9/raw/a5820379ec6eae76ee792495cc5dd1685c977a73/vendedores.json';
d3.json(url).then(function(data) {
data.forEach(d =>
Object.assign(d, {
mes: d.Month,
atual: d.Vendas_Ano,
passado: d.Vendas_Ant
})
);
var cf = crossfilter(data);
vendedorDim = cf.dimension(function(d) {
return d.vendnm;
});
var vendedorGroup = vendedorDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
citiesDim = cf.dimension(function(d) {
return d.zona;
});
var citiesGroup = citiesDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
const totalAll = cf.groupAll()
.reduce(reduceAdd, reduceRemove, reduceInitial);
debugger;
var dim = cf.dimension(dc.pluck('mes')),
grp1 = dim.group().reduceSum(dc.pluck('atual')),
grp2 = dim.group().reduceSum(dc.pluck('passado'));
var minMonth = dim.bottom(1)[0].mes;
var maxMonth = dim.top(1)[0].mes;
var all = cf.groupAll();
dc.dataCount(".dc-data-count")
.dimension(cf)
.group(all);
function reduceAdd(p, v) {
p.totalAno += +v.Vendas_Ano;
p.totalHomologo += +v.Vendas_Ant;
return p;
}
function reduceRemove(p, v) {
p.totalAno -= v.Vendas_Ano;
p.totalHomologo -= v.Vendas_Ant;
return p;
}
function reduceInitial() {
return {
totalAno: 0,
totalHomologo: 0,
};
}
// Fake Dimension
rank = function(p) {
return ""
};
// Chart by months
composite
.width(window.innerWidth - adjustX)
.height(300)
.x(d3.scaleLinear().domain([1, 12]))
.yAxisLabel("")
.xAxisLabel("Month")
.legend(dc.legend().x(500).y(0).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.lineChart(composite)
.dimension(dim)
.colors('steelblue')
.group(grp1, "Currently Year"),
dc.lineChart(composite)
.dimension(dim)
.colors('darkorange')
.group(grp2, "Last Year")
])
.brushOn(true);
composite.brush().extent([-0.5, data.length + 1.5])
composite.extendBrush = function(brushSelection) {
if (brushSelection) {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
const point = Math.round((brushSelection[0] + brushSelection[1]) / 2);
return [
point - 0.5,
point + 0.5
];
}
};
// Sales Table
vendedorTable.width(500)
.height(480)
.dimension(remove_empty_bins(vendedorGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// vendedors totals
const totalCYDisplay = dc.numberDisplay('#total-current-year')
.group(totalAll)
.valueAccessor(d => d.totalAno)
const totalLYDisplay = dc.numberDisplay('#total-last-year')
.group(totalAll)
.valueAccessor(d => d.totalHomologo)
// Cities Table
citiesTable
.width(500)
.height(480)
.dimension(remove_empty_bins(citiesGroup))
.group(rank)
.columns([function(d) {
return d.key;
},
function(d) {
return Number(Math.round(d.value.totalAno * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
},
function(d) {
return Number(Math.round(d.value.totalHomologo * 100) / 100).toLocaleString("es-ES", {
minimumFractionDigits: 2
}) + '€';
}
])
.controlsUseVisibility(true)
.sortBy(function(d) {
return d.value.totalAno
})
.order(d3.descending)
// Sales click events
vendedorTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
vendedorDim.filter(null);
else
vendedorDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
// Cities click events
citiesTable.on('pretransition', function(table) {
table.selectAll('td.dc-table-column')
.on('click', function(d) {
let filters = table.filters().slice();
if (filters.indexOf(d.key) === -1)
filters.push(d.key);
else
filters = filters.filter(k => k != d.key);
if (filters.length === 0)
citiesDim.filter(null);
else
citiesDim.filterFunction(function(d) {
return filters.indexOf(d) !== -1;
})
table.replaceFilter([filters]);
vendedorTable.filter(null);
vendedorDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
let filters = table.filters();
table.selectAll('tr.dc-table-row')
.classed('sel-rows', d => filters.indexOf(d.key) !== -1);
});
dc.renderAll();
// reset functions
$('#reset').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
$('#resetTable2').on('click', function() {
vendedorTable.filter(null);
vendedorDim.filter(null);
citiesTable.filter(null);
citiesDim.filter(null);
composite.filter(null);
dc.redrawAll();
});
/****************************************************************************/
// Functions to handle responsive
apply_resizing(composite, adjustX, adjustY, function(composite) {
composite.legend().x(window.innerWidth - 200);
});
var find_query = function() {
var _map = window.location.search.substr(1).split('&').map(function(a) {
return a.split('=');
}).reduce(function(p, v) {
if (v.length > 1)
p[v[0]] = decodeURIComponent(v[1].replace(/\+/g, " "));
else
p[v[0]] = true;
return p;
}, {});
return function(field) {
return _map[field] || null;
};
}();
var resizeMode = find_query('resize') || 'widhei';
function apply_resizing(composite, adjustX, adjustY, onresize) {
if (resizeMode === 'viewbox') {
composite
.width(300)
.height(200)
.useViewBoxResizing(true);
d3.select(composite.anchor()).classed('fullsize', false);
} else {
adjustX = adjustX || 0;
adjustY = adjustY || adjustX || 0;
composite
.width(window.innerWidth - adjustX)
.height(300);
window.onresize = function() {
if (onresize) {
onresize(composite);
}
composite
.width(window.innerWidth - adjustX)
.height(300);
if (composite.rescale) {
composite.rescale();
}
composite.redraw();
};
}
}
});
#composite {
padding: 10px;
}
.dc-table-group {
visibility: collapse;
}
tr.dc-table-row.sel-rows {
background-color: lightblue;
}
.brush .custom-brush-handle {
display: none;
}
<head>
<!-- favicon -->
<link rel="shortcut icon" href="https://img.icons8.com/nolan/64/puzzle.png">
<!-- bootstrap.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- bootstrap-theme.css -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- dc.css -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/2.1.8/dc.css">
<!-- jquery.js -->
<script src="https://code.jquery.com/jquery-3.4.1.js" integrity="sha256-WpOohJOqMqqyKL9FccASB9O0KwACQJpFTUBLTYOVvVU=" crossorigin="anonymous"></script>
<!-- bootstrap.js -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<!-- d3.v5.js -->
<script src="https://d3js.org/d3.v5.js"></script>
<!-- crossfilter.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<!-- dc.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.1.8/dc.js"></script>
<title>Chart</title>
</head>
<body>
<div id="composite"></div>
<div class="container-fluid">
<div class="row content">
<div class="col-md-12">
<div class="col-md-6">
<table class="table" id="totaisVendedores">
<thead>
<tr>
<th>Total Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="totaisCities">
<thead>
<tr>
<th>Total City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
<div class="col-md-6">
<table class="table" id="vendedores">
<thead>
<tr>
<th>Sales</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
<tfoot>
<tr>
<th>TOTAL</th>
<th id="total-current-year"></th>
<th id="total-last-year"></th>
</tr>
</tfoot>
</table>
</div>
<div class="col-md-6">
<table class="table" id="cities">
<thead>
<tr>
<th>City</th>
<th>Current Year</th>
<th>Last Year</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</body>
推荐阅读
- c# - 如何仅在 Visual Studio 中启用当前文件的断点?
- mysql - Windows中的mysql差异/增量备份
- python - Python 通过 1 次调用设置更多属性
- spring-boot - RequestHandlerRetryAdvice 无法在 Spring Integration 中与 Ftp.outboundGateway 一起使用
- ios - 在 Mapbox iOS SDK 的标记下移动地图
- spring - 当我使用 ng build 时,proxy.conf.json 在 Angular 6 中不起作用
- android - 仅在某些活动中的操作栏?
- ruby-on-rails - 在 Rails 中添加复杂的查询
- python - 试图总结投资金额,但一直说不可迭代
- ansible - 确定演出主持人