|
@@ -0,0 +1,131 @@
|
|
|
+/**
|
|
|
+ * 统计分析页面的JavaScript功能
|
|
|
+ */
|
|
|
+
|
|
|
+// 初始化所有图表
|
|
|
+function initCharts() {
|
|
|
+ // 各图表初始化逻辑在页面内已实现
|
|
|
+
|
|
|
+ // 添加图表交互功能
|
|
|
+ enableChartInteractions();
|
|
|
+
|
|
|
+ // 初始化数据表格排序功能
|
|
|
+ initDataTableSorting();
|
|
|
+}
|
|
|
+
|
|
|
+// 为图表添加交互功能
|
|
|
+function enableChartInteractions() {
|
|
|
+ // 为所有图表添加下载功能
|
|
|
+ addChartDownloadButtons();
|
|
|
+
|
|
|
+ // 为折线图添加时间范围选择功能
|
|
|
+ addTimeRangeSelectors();
|
|
|
+}
|
|
|
+
|
|
|
+// 添加图表下载按钮
|
|
|
+function addChartDownloadButtons() {
|
|
|
+ const chartContainers = document.querySelectorAll('.chart-container');
|
|
|
+
|
|
|
+ chartContainers.forEach(container => {
|
|
|
+ const canvas = container.querySelector('canvas');
|
|
|
+ if (!canvas) return;
|
|
|
+
|
|
|
+ const header = container.querySelector('.chart-header');
|
|
|
+ const downloadBtn = document.createElement('button');
|
|
|
+ downloadBtn.className = 'btn btn-sm';
|
|
|
+ downloadBtn.innerHTML = '下载图表';
|
|
|
+ downloadBtn.onclick = function() {
|
|
|
+ const chartInstance = Chart.getChart(canvas);
|
|
|
+ if (!chartInstance) return;
|
|
|
+
|
|
|
+ // 创建临时链接并触发下载
|
|
|
+ const a = document.createElement('a');
|
|
|
+ a.href = chartInstance.toBase64Image();
|
|
|
+ a.download = (container.querySelector('.chart-title').textContent || 'chart') + '.png';
|
|
|
+ document.body.appendChild(a);
|
|
|
+ a.click();
|
|
|
+ document.body.removeChild(a);
|
|
|
+ };
|
|
|
+
|
|
|
+ header.appendChild(downloadBtn);
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 添加时间范围选择器
|
|
|
+function addTimeRangeSelectors() {
|
|
|
+ // 为具有时间轴的图表添加缩放功能
|
|
|
+ // 这个功能需要Chart.js的Zoom插件,如果需要可以进一步实现
|
|
|
+}
|
|
|
+
|
|
|
+// 初始化数据表格排序
|
|
|
+function initDataTableSorting() {
|
|
|
+ const tables = document.querySelectorAll('.data-table');
|
|
|
+
|
|
|
+ tables.forEach(table => {
|
|
|
+ const headers = table.querySelectorAll('th');
|
|
|
+
|
|
|
+ headers.forEach((header, index) => {
|
|
|
+ // 排除不需要排序的列
|
|
|
+ if (header.classList.contains('no-sort')) return;
|
|
|
+
|
|
|
+ header.style.cursor = 'pointer';
|
|
|
+ header.dataset.sortDirection = 'none'; // none, asc, desc
|
|
|
+
|
|
|
+ // 添加排序图标
|
|
|
+ const sortIcon = document.createElement('span');
|
|
|
+ sortIcon.className = 'sort-icon';
|
|
|
+ sortIcon.innerHTML = ' ⇅';
|
|
|
+ header.appendChild(sortIcon);
|
|
|
+
|
|
|
+ // 添加点击事件
|
|
|
+ header.addEventListener('click', () => {
|
|
|
+ // 重置其他列的排序状态
|
|
|
+ headers.forEach(h => {
|
|
|
+ if (h !== header) h.dataset.sortDirection = 'none';
|
|
|
+ });
|
|
|
+
|
|
|
+ // 切换当前列的排序方向
|
|
|
+ const currentDirection = header.dataset.sortDirection;
|
|
|
+ if (currentDirection === 'none' || currentDirection === 'desc') {
|
|
|
+ header.dataset.sortDirection = 'asc';
|
|
|
+ } else {
|
|
|
+ header.dataset.sortDirection = 'desc';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 执行排序
|
|
|
+ sortTable(table, index, header.dataset.sortDirection);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 执行表格排序
|
|
|
+function sortTable(table, columnIndex, direction) {
|
|
|
+ const tbody = table.querySelector('tbody');
|
|
|
+ const rows = Array.from(tbody.querySelectorAll('tr'));
|
|
|
+
|
|
|
+ // 排序行
|
|
|
+ rows.sort((a, b) => {
|
|
|
+ const aValue = a.querySelectorAll('td')[columnIndex].textContent.trim();
|
|
|
+ const bValue = b.querySelectorAll('td')[columnIndex].textContent.trim();
|
|
|
+
|
|
|
+ // 检测是否为数值(包括货币符号)
|
|
|
+ const aNum = parseFloat(aValue.replace(/[¥,]/g, ''));
|
|
|
+ const bNum = parseFloat(bValue.replace(/[¥,]/g, ''));
|
|
|
+
|
|
|
+ if (!isNaN(aNum) && !isNaN(bNum)) {
|
|
|
+ return direction === 'asc' ? aNum - bNum : bNum - aNum;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 字符串比较
|
|
|
+ return direction === 'asc'
|
|
|
+ ? aValue.localeCompare(bValue, 'zh-CN')
|
|
|
+ : bValue.localeCompare(aValue, 'zh-CN');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 重新添加排序后的行
|
|
|
+ rows.forEach(row => tbody.appendChild(row));
|
|
|
+}
|
|
|
+
|
|
|
+// 页面加载完成后初始化
|
|
|
+document.addEventListener('DOMContentLoaded', initCharts);
|