在一份气候报告中,看到如下的表格。这个表格在交互上很有新意,左上角有输入框可以按关键字查询,右上角有翻页按钮,可以分页浏览。
来源:https://globalinequality.org/responsibility-for-climate-breakdown/
Power BI使用DAX结合HTML可以实现类似的效果,如下图所示。搜索结果是实时的,比如输入第一个字“武”,就会自动筛选姓名带有“武”的所有行。
DAX驱动可视化,度量值如下,把度量值中的维度、KPI换成你模型中的数据,放入HTML Content视觉对象使用。
HTML表格.分页+搜索 =
VAR t =
ADDCOLUMNS (
VALUES ( 'B 销售数据'[销售员] ),
"销售业绩", FORMAT ( [M.销售业绩], "#,#" ),
"销售折扣", FORMAT ( [M.销售折扣], "0.00" ),
"连带率", FORMAT ( [M.客单量], "0.00" ),
"客单价", FORMAT ( [M.客单价], "#,#" )
)
VAR HTML_Text =
CONCATENATEX (
t,
"<tr><td style='text-align:left'>" & [销售员] & "</td><td style='text-align:right'>" & [销售业绩] & "</td><td style='text-align:right'>" & [销售折扣] & "</td><td style='text-align:right'>" & [连带率] & "</td><td style='text-align:right'>" & [客单价] & "</td></tr>",
,[M.销售业绩],DESC
)
RETURN "
<head>
<style>
table {
font-family: Arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
th {
background-color: #f2f2f2;
text-align: left;
padding: 8px;
}
td {
padding: 8px;
border-bottom: 1px solid #ddd;
}
th:nth-child(1) {
text-align: left;
}
th:nth-child(n+2) {
text-align: right;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.search-box {
padding: 6px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 200px;
}
.pagination {
display: flex;
justify-content: flex-end;
}
.pagination a {
color: black;
padding: 6px 12px;
text-decoration: none;
border: 1px solid #ddd;
margin: 0 2px;
border-radius: 4px;
}
.pagination a.active {
background-color: #4CAF50;
color: white;
border: 1px solid #4CAF50;
}
.pagination a:hover:not(.active) {
background-color: #ddd;
}
</style>
</head>
<body>
<div class='table-header'>
<input type='text' id='searchInput' class='search-box' placeholder='搜索姓名...'>
<div class='pagination' id='pagination'></div>
</div>
<table id='pagedTable'>
<thead>
<tr>
<th>姓名</th>
<th>销售业绩</th>
<th>销售折扣</th>
<th>连带率</th>
<th>客单价</th>
</tr>
</thead>
<tbody>" &
HTML_Text & "
</tbody>
</table>
<script>
function paginateTable() {
const table = document.getElementById('pagedTable');
let rows = Array.from(table.querySelectorAll('tbody tr'));
const rowsPerPage = 10;
let pageCount = Math.ceil(rows.length / rowsPerPage);
const paginationDiv = document.getElementById('pagination');
const searchInput = document.getElementById('searchInput');
let currentPage = 1;
function filterRows() {
const searchTerm = searchInput.value.toLowerCase();
rows.forEach(row => {
const nameCell = row.querySelector('td:first-child');
const name = nameCell.textContent.toLowerCase();
row.style.display = name.includes(searchTerm) ? '' : 'none';
});
const visibleRows = rows.filter(row => row.style.display !== 'none');
pageCount = Math.ceil(visibleRows.length / rowsPerPage);
currentPage = Math.min(currentPage, pageCount || 1);
showPage(currentPage);
}
function showPage(page) {
const searchTerm = searchInput.value.toLowerCase();
const visibleRows = rows.filter(row => {
if (searchTerm) {
const nameCell = row.querySelector('td:first-child');
const name = nameCell.textContent.toLowerCase();
return name.includes(searchTerm);
}
return true;
});
const start = (page - 1) * rowsPerPage;
const end = start + rowsPerPage;
rows.forEach(row => {
row.style.display = 'none';
});
visibleRows.slice(start, end).forEach(row => {
row.style.display = '';
});
updatePaginationButtons(page);
}
function updatePaginationButtons(page) {
paginationDiv.innerHTML = '';
currentPage = page;
if (page > 1) {
const prevLink = document.createElement('a');
prevLink.href = '#';
prevLink.innerHTML = '«';
prevLink.addEventListener('click', (e) => {
e.preventDefault();
showPage(page - 1);
});
paginationDiv.appendChild(prevLink);
}
// 公众号wujunmin
const maxVisiblePages = 5;
let startPage = Math.max(1, page - Math.floor(maxVisiblePages / 2));
let endPage = startPage + maxVisiblePages - 1;
if (endPage > pageCount) {
endPage = pageCount;
startPage = Math.max(1, endPage - maxVisiblePages + 1);
}
if (startPage > 1) {
const firstLink = document.createElement('a');
firstLink.href = '#';
firstLink.textContent = '1';
firstLink.addEventListener('click', (e) => {
e.preventDefault();
showPage(1);
});
paginationDiv.appendChild(firstLink);
if (startPage > 2) {
const ellipsis = document.createElement('span');
ellipsis.textContent = '...';
ellipsis.style.padding = '6px 12px';
paginationDiv.appendChild(ellipsis);
}
}
for (let i = startPage; i <= endPage; i++) {
const pageLink = document.createElement('a');
pageLink.href = '#';
pageLink.textContent = i;
if (i === page) {
pageLink.className = 'active';
}
pageLink.addEventListener('click', (e) => {
e.preventDefault();
showPage(i);
});
paginationDiv.appendChild(pageLink);
}
if (endPage < pageCount) {
if (endPage < pageCount - 1) {
const ellipsis = document.createElement('span');
ellipsis.textContent = '...';
ellipsis.style.padding = '6px 12px';
paginationDiv.appendChild(ellipsis);
}
const lastLink = document.createElement('a');
lastLink.href = '#';
lastLink.textContent = pageCount;
lastLink.addEventListener('click', (e) => {
e.preventDefault();
showPage(pageCount);
});
paginationDiv.appendChild(lastLink);
}
if (page < pageCount) {
const nextLink = document.createElement('a');
nextLink.href = '#';
nextLink.innerHTML = '»';
nextLink.addEventListener('click', (e) => {
e.preventDefault();
showPage(page + 1);
});
paginationDiv.appendChild(nextLink);
}
}
searchInput.addEventListener('input', function() {
filterRows();
});
showPage(1);
}
setTimeout(paginateTable, 100);
</script>
</body>"
为知识星球会员额外提供一种带数据条的版本: