首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Power BI表格高级交互:实时搜索+分页叠加

Power BI表格高级交互:实时搜索+分页叠加

作者头像
wujunmin
发布2025-07-10 09:23:19
发布2025-07-10 09:23:19
16300
代码可运行
举报
文章被收录于专栏:wujunminwujunmin
运行总次数:0
代码可运行

在一份气候报告中,看到如下的表格。这个表格在交互上很有新意,左上角有输入框可以按关键字查询,右上角有页按钮,可以分页浏览。

图片
图片

来源:https://globalinequality.org/responsibility-for-climate-breakdown/

Power BI使用DAX结合HTML可以实现类似的效果,如下图所示。搜索结果是实时的,比如输入第一个字“武”,就会自动筛选姓名带有“武”的所有行。

图片
图片

DAX驱动可视化,度量值如下,把度量值中的维度、KPI换成你模型中的数据,放入HTML Content视觉对象使用。

代码语言:javascript
代码运行次数:0
运行
复制
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 = '&laquo;';
            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 = '&raquo;';
            nextLink.addEventListener('click', (e) => {
                e.preventDefault();
                showPage(page + 1);
            });
            paginationDiv.appendChild(nextLink);
        }
    }
    
    searchInput.addEventListener('input', function() {
        filterRows();
    });
    
    showPage(1);
}
setTimeout(paginateTable, 100);
</script>
</body>"

为知识星球会员额外提供一种带数据条的版本:

图片
图片
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-07-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 wujunmin 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档