文章目录[隐藏]
一步步实现:在WordPress中添加实时数据大屏与可视化图表
引言:为什么WordPress需要实时数据可视化功能
在当今数据驱动的互联网时代,实时数据可视化已成为网站运营和内容展示的重要组成部分。WordPress作为全球最流行的内容管理系统,虽然拥有强大的内容管理能力,但在原生功能上却缺乏专业的数据可视化工具。许多网站运营者、电商店主和内容创作者都需要在自己的WordPress站点上展示实时数据,如网站流量统计、销售数据、用户行为分析等。
传统上,用户可能需要依赖第三方服务或插件来实现这些功能,但这往往带来数据安全、加载速度和定制灵活性等问题。通过WordPress代码二次开发,我们可以创建完全可控、高度定制化的实时数据大屏和可视化图表,不仅能提升网站的专业形象,还能为访客提供更有价值的数据洞察。
本文将详细介绍如何通过代码开发,在WordPress中实现实时数据大屏与可视化图表功能,涵盖从环境准备到最终部署的完整流程。
第一章:开发环境准备与项目规划
1.1 开发环境搭建
在开始开发之前,我们需要准备合适的开发环境。建议使用本地开发环境,如Local by Flywheel、XAMPP或MAMP,这样可以避免对生产站点造成影响。
首先,确保你的开发环境满足以下要求:
- PHP 7.4或更高版本
- MySQL 5.6或更高版本
- WordPress 5.8或更高版本
- 支持HTTPS(用于某些API调用)
接下来,创建一个专门用于开发的WordPress主题或插件目录。考虑到功能的独立性,我们建议将其开发为一个独立插件,这样可以在不同主题间保持功能一致性。
<?php
/**
* Plugin Name: 实时数据大屏与可视化工具
* Plugin URI: https://yourwebsite.com/
* Description: 为WordPress添加实时数据大屏和可视化图表功能
* Version: 1.0.0
* Author: Your Name
* License: GPL v2 or later
*/
1.2 项目架构设计
良好的项目架构是成功开发的关键。我们建议采用模块化设计,将功能分解为以下几个核心模块:
- 数据获取模块:负责从各种数据源收集数据
- 数据处理模块:对原始数据进行清洗、转换和聚合
- 图表渲染模块:使用JavaScript库生成可视化图表
- 前端展示模块:创建数据大屏的界面布局
- 管理后台模块:提供配置选项和管理界面
目录结构建议如下:
real-time-dashboard/
├── includes/
│ ├── class-data-fetcher.php
│ ├── class-data-processor.php
│ ├── class-chart-renderer.php
│ └── class-admin-settings.php
├── assets/
│ ├── css/
│ ├── js/
│ └── images/
├── templates/
│ ├── dashboard-template.php
│ └── widget-template.php
└── real-time-dashboard.php
1.3 技术选型与工具准备
对于数据可视化,我们需要选择合适的JavaScript图表库。以下是几个优秀的选择:
- Chart.js:轻量级、易于使用,适合基础图表
- ECharts:功能强大、图表类型丰富,由百度开发
- D3.js:最灵活、功能最强大,但学习曲线较陡
- ApexCharts:现代、交互式图表库
考虑到易用性和功能平衡,本文选择ECharts作为主要图表库。同时,我们还需要以下工具:
- 用于实时数据更新的WebSocket或Server-Sent Events
- 用于数据缓存的Transients API或Redis
- 用于REST API开发WordPress的REST API功能
第二章:数据获取与处理模块开发
2.1 创建数据获取类
数据获取是实时数据大屏的基础。我们需要创建一个专门的数据获取类,负责从不同数据源收集信息。
<?php
class RealTime_Data_Fetcher {
private $data_sources;
public function __construct() {
$this->data_sources = array(
'website_traffic' => array(
'name' => '网站流量',
'type' => 'internal',
'callback' => array($this, 'fetch_website_traffic')
),
'woocommerce_sales' => array(
'name' => '销售数据',
'type' => 'woocommerce',
'callback' => array($this, 'fetch_woocommerce_sales')
),
'external_api' => array(
'name' => '外部API数据',
'type' => 'external',
'callback' => array($this, 'fetch_external_data')
)
);
}
/**
* 获取网站流量数据
*/
public function fetch_website_traffic($time_range = 'today') {
global $wpdb;
$data = array();
$current_time = current_time('timestamp');
switch($time_range) {
case 'today':
$start_time = strtotime('today midnight');
break;
case 'week':
$start_time = strtotime('-7 days');
break;
case 'month':
$start_time = strtotime('-30 days');
break;
default:
$start_time = strtotime('-24 hours');
}
// 查询页面访问数据
$query = $wpdb->prepare(
"SELECT COUNT(*) as count, DATE_FORMAT(visit_time, '%%H:00') as hour
FROM {$wpdb->prefix}page_visits
WHERE visit_time >= %s
GROUP BY HOUR(visit_time)
ORDER BY visit_time",
date('Y-m-d H:i:s', $start_time)
);
$results = $wpdb->get_results($query);
foreach($results as $row) {
$data[] = array(
'time' => $row->hour,
'visits' => (int)$row->count
);
}
return $data;
}
/**
* 获取WooCommerce销售数据
*/
public function fetch_woocommerce_sales($time_range = 'today') {
if (!class_exists('WooCommerce')) {
return array();
}
$data = array();
// 根据时间范围获取订单数据
$args = array(
'status' => array('completed', 'processing'),
'date_created' => '>' . (time() - 24*60*60), // 最近24小时
'limit' => -1
);
$orders = wc_get_orders($args);
$hourly_sales = array();
foreach($orders as $order) {
$order_time = $order->get_date_created()->getTimestamp();
$hour = date('H:00', $order_time);
$total = $order->get_total();
if (!isset($hourly_sales[$hour])) {
$hourly_sales[$hour] = 0;
}
$hourly_sales[$hour] += $total;
}
ksort($hourly_sales);
foreach($hourly_sales as $hour => $sales) {
$data[] = array(
'time' => $hour,
'sales' => $sales
);
}
return $data;
}
/**
* 获取所有数据源的数据
*/
public function fetch_all_data() {
$all_data = array();
foreach($this->data_sources as $key => $source) {
if (is_callable($source['callback'])) {
$all_data[$key] = call_user_func($source['callback']);
}
}
return $all_data;
}
}
2.2 实现数据缓存机制
为了避免频繁查询数据库,我们需要实现数据缓存机制。WordPress提供了Transients API,可以方便地实现临时数据存储。
class RealTime_Data_Processor {
private $cache_expiration = 300; // 5分钟缓存
/**
* 处理并缓存数据
*/
public function process_and_cache_data($data_key, $raw_data) {
// 数据处理逻辑
$processed_data = $this->process_data($raw_data);
// 设置缓存
set_transient(
'rtd_' . $data_key,
$processed_data,
$this->cache_expiration
);
return $processed_data;
}
/**
* 数据处理逻辑
*/
private function process_data($raw_data) {
$processed = array();
// 数据清洗和转换
foreach($raw_data as $item) {
// 移除空值
$cleaned_item = array_filter($item, function($value) {
return $value !== null && $value !== '';
});
// 数据格式化
if (!empty($cleaned_item)) {
$processed[] = $this->format_data($cleaned_item);
}
}
// 数据聚合(如果需要)
if (count($processed) > 100) {
$processed = $this->aggregate_data($processed);
}
return $processed;
}
/**
* 数据格式化
*/
private function format_data($data) {
// 根据数据类型进行格式化
foreach($data as $key => $value) {
if (is_numeric($value)) {
$data[$key] = floatval($value);
} elseif (is_string($value)) {
$data[$key] = sanitize_text_field($value);
}
}
return $data;
}
}
2.3 创建REST API端点
为了前端能够获取数据,我们需要创建REST API端点。
class RealTime_Dashboard_API {
public function __construct() {
add_action('rest_api_init', array($this, 'register_routes'));
}
public function register_routes() {
// 获取所有数据
register_rest_route('real-time-dashboard/v1', '/data', array(
'methods' => 'GET',
'callback' => array($this, 'get_all_data'),
'permission_callback' => array($this, 'check_permissions')
));
// 获取特定数据源的数据
register_rest_route('real-time-dashboard/v1', '/data/(?P<source>[a-zA-Z0-9_-]+)', array(
'methods' => 'GET',
'callback' => array($this, 'get_source_data'),
'permission_callback' => array($this, 'check_permissions')
));
}
public function get_all_data($request) {
$fetcher = new RealTime_Data_Fetcher();
$processor = new RealTime_Data_Processor();
$all_data = array();
$sources = array('website_traffic', 'woocommerce_sales');
foreach($sources as $source) {
$raw_data = $fetcher->fetch_data_by_source($source);
$processed_data = $processor->process_and_cache_data($source, $raw_data);
$all_data[$source] = $processed_data;
}
return rest_ensure_response($all_data);
}
public function check_permissions($request) {
// 检查用户权限,可以根据需要调整
return current_user_can('edit_posts');
}
}
第三章:前端可视化界面开发
3.1 创建数据大屏基础布局
首先,我们需要创建一个响应式的大屏布局,确保在不同设备上都能良好显示。
<!-- templates/dashboard-template.php -->
<div class="real-time-dashboard-container">
<header class="dashboard-header">
<h1>实时数据大屏</h1>
<div class="dashboard-controls">
<select id="timeRangeSelect" class="dashboard-select">
<option value="realtime">实时</option>
<option value="today">今日</option>
<option value="week">本周</option>
<option value="month">本月</option>
</select>
<button id="refreshBtn" class="dashboard-button">
<span class="refresh-icon">↻</span> 刷新
</button>
<button id="fullscreenBtn" class="dashboard-button">
<span class="fullscreen-icon">⛶</span> 全屏
</button>
</div>
</header>
<div class="dashboard-grid">
<!-- 图表容器将通过JavaScript动态生成 -->
<div class="grid-item" data-chart-type="line" data-source="website_traffic">
<div class="chart-header">
<h3>网站实时访问量</h3>
<div class="chart-stats">
<span class="current-value">0</span>
<span class="change-indicator"></span>
</div>
</div>
<div class="chart-container" id="trafficChart"></div>
</div>
<div class="grid-item" data-chart-type="bar" data-source="woocommerce_sales">
<div class="chart-header">
<h3>实时销售数据</h3>
<div class="chart-stats">
<span class="current-value">¥0</span>
<span class="change-indicator"></span>
</div>
</div>
<div class="chart-container" id="salesChart"></div>
</div>
<!-- 更多图表容器... -->
</div>
<div class="dashboard-footer">
<div class="last-updated">
最后更新: <span id="lastUpdatedTime">--:--:--</span>
</div>
<div class="data-source-info">
数据每5分钟自动更新
</div>
</div>
</div>
3.2 引入ECharts并初始化图表
接下来,我们需要引入ECharts库并创建图表初始化函数。
// assets/js/dashboard-main.js
(function($) {
'use strict';
class RealTimeDashboard {
constructor() {
this.charts = {};
this.dataCache = {};
this.updateInterval = 300000; // 5分钟
this.isFullscreen = false;
this.timeRange = 'realtime';
this.init();
}
init() {
this.loadECharts();
this.initCharts();
this.bindEvents();
this.startAutoUpdate();
this.fetchInitialData();
}
loadECharts() {
// 动态加载ECharts库
if (typeof echarts === 'undefined') {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js';
script.onload = () => this.onEChartsLoaded();
document.head.appendChild(script);
} else {
this.onEChartsLoaded();
}
}
onEChartsLoaded() {
console.log('ECharts loaded successfully');
this.echarts = echarts;
this.initCharts();
}
initCharts() {
if (!this.echarts) return;
// 初始化所有图表容器
$('.chart-container').each((index, container) => {
const chartId = $(container).attr('id');
const chartType = $(container).closest('.grid-item').data('chart-type');
const dataSource = $(container).closest('.grid-item').data('source');
this.charts[chartId] = this.echarts.init(container);
this.setChartOption(chartId, chartType, dataSource);
});
// 监听窗口大小变化,重新调整图表大小
$(window).on('resize', () => {
this.resizeCharts();
});
}
setChartOption(chartId, chartType, dataSource) {
const baseOption = {
tooltip: {
trigger: 'axis',
formatter: function(params) {
let result = `${params[0].axisValue}<br/>`;
params.forEach(param => {
result += `${param.marker} ${param.seriesName}: ${param.value}<br/>`;
});
return result;
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
top: '15%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: []
},
yAxis: {
type: 'value'
},
series: []
};
// 根据图表类型设置不同选项
switch(chartType) {
case 'line':
baseOption.series.push({
name: '数据',
type: 'line',
smooth: true,
data: [],
itemStyle: {
color: '#5470c6'
},
areaStyle: {
color: new this.echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(84, 112, 198, 0.5)' },
{ offset: 1, color: 'rgba(84, 112, 198, 0.1)' }
])
}
});
break;
case 'bar':
baseOption.series.push({
name: '数据',
type: 'bar',
data: [],
itemStyle: {
color: new this.echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#83bff6' },
{ offset: 0.5, color: '#188df0' },
{ offset: 1, color: '#188df0' }
])
}
});
break;
// 可以添加更多图表类型
}
this.charts[chartId].setOption(baseOption);
}
fetchInitialData() {
this.fetchData();
}
fetchData() {
const self = this;
$.ajax({
url: '/wp-json/real-time-dashboard/v1/data',
method: 'GET',
dataType: 'json',
beforeSend: function(xhr) {
xhr.setRequestHeader('X-WP-Nonce', realTimeDashboard.nonce);
},
success: function(response) {
self.processData(response);
self.updateLastUpdatedTime();
},
error: function(xhr, status, error) {
console.error('数据获取失败:', error);
self.showErrorMessage('数据加载失败,请稍后重试');
}
});
}
processData(data) {
// 处理网站流量数据
if (data.website_traffic && this.charts.trafficChart) {
this.updateChartData('trafficChart', data.website_traffic, '访问量');
this.updateStats('trafficChart', data.website_traffic);
}
// 处理销售数据
if (data.woocommerce_sales && this.charts.salesChart) {
this.updateChartData('salesChart', data.woocommerce_sales, '销售额');
this.updateStats('salesChart', data.woocommerce_sales);
}
// 可以添加更多数据处理逻辑
}
updateChartData(chartId, data, seriesName) {
const chart = this.charts[chartId];
if (!chart) return;
const xData = [];
const yData = [];
data.forEach(item => {
xData.push(item.time || item.date);
yData.push(item.visits || item.sales || item.value);
});
const option = chart.getOption();
option.xAxis[0].data = xData;
option.series[0].data = yData;
option.series[0].name = seriesName;
chart.setOption(option);
}
updateStats(chartId, data) {
if (!data || data.length === 0) return;
const currentValue = data[data.length - 1].visits ||
data[data.length - 1].sales ||
data[data.length - 1].value;
const previousValue = data.length > 1 ?
data[data.length - 2].visits ||
data[data.length - 2].sales ||
data[data.length - 2].value : 0;
const change = previousValue > 0 ?
((currentValue - previousValue) / previousValue * 100).toFixed(1) : 0;
const $gridItem = $(`#${chartId}`).closest('.grid-item');
const $currentValue = $gridItem.find('.current-value');
const $changeIndicator = $gridItem.find('.change-indicator');
// 更新当前值
$currentValue.text(this.formatValue(currentValue, chartId));
// 更新变化指示器
if (change > 0) {
$changeIndicator.html(`<span class="increase">↑ ${change}%</span>`);
$changeIndicator.removeClass('decrease').addClass('increase');
} else if (change < 0) {
$changeIndicator.html(`<span class="decrease">↓ ${Math.abs(change)}%</span>`);
$changeIndicator.removeClass('increase').addClass('decrease');
} else {
$changeIndicator.html('<span class="neutral">→ 0%</span>');
$changeIndicator.removeClass('increase decrease');
}
}
formatValue(value, chartId) {
if (chartId === 'salesChart') {
return '¥' + Number(value).toLocaleString('zh-CN', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
}
return Number(value).toLocaleString('zh-CN');
}
bindEvents() {
const self = this;
// 刷新按钮
$('#refreshBtn').on('click', function() {
self.fetchData();
$(this).addClass('refreshing');
setTimeout(() => {
$(this).removeClass('refreshing');
}, 1000);
});
// 时间范围选择
$('#timeRangeSelect').on('change', function() {
self.timeRange = $(this).val();
self.fetchData();
});
// 全屏按钮
$('#fullscreenBtn').on('click', function() {
self.toggleFullscreen();
});
// 键盘快捷键
$(document).on('keydown', function(e) {
if (e.key === 'F11' || (e.key === 'f' && e.ctrlKey)) {
e.preventDefault();
self.toggleFullscreen();
} else if (e.key === 'r' && e.ctrlKey) {
e.preventDefault();
self.fetchData();
}
});
}
toggleFullscreen() {
const container = $('.real-time-dashboard-container')[0];
if (!this.isFullscreen) {
if (container.requestFullscreen) {
container.requestFullscreen();
} else if (container.webkitRequestFullscreen) {
container.webkitRequestFullscreen();
} else if (container.msRequestFullscreen) {
container.msRequestFullscreen();
}
this.isFullscreen = true;
$('#fullscreenBtn').html('<span class="exit-fullscreen-icon">⛶</span> 退出全屏');
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
this.isFullscreen = false;
$('#fullscreenBtn').html('<span class="fullscreen-icon">⛶</span> 全屏');
}
}
resizeCharts() {
Object.values(this.charts).forEach(chart => {
chart.resize();
});
}
startAutoUpdate() {
setInterval(() => {
this.fetchData();
}, this.updateInterval);
}
updateLastUpdatedTime() {
const now = new Date();
const timeString = now.toLocaleTimeString('zh-CN', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
$('#lastUpdatedTime').text(timeString);
}
showErrorMessage(message) {
// 显示错误提示
const $errorDiv = $('<div class="dashboard-error">')
.text(message)
.hide()
.appendTo('.dashboard-header');
$errorDiv.fadeIn(300);
setTimeout(() => {
$errorDiv.fadeOut(300, function() {
$(this).remove();
});
}, 5000);
}
}
// 初始化仪表板
$(document).ready(function() {
if ($('.real-time-dashboard-container').length) {
window.realTimeDashboardApp = new RealTimeDashboard();
}
});
})(jQuery);
3.3 添加CSS样式
/* assets/css/dashboard-style.css */
.real-time-dashboard-container {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
color: #ffffff;
min-height: 100vh;
padding: 20px;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.dashboard-header h1 {
margin: 0;
font-size: 28px;
font-weight: 600;
background: linear-gradient(90deg, #4cc9f0, #4361ee);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.dashboard-controls {
display: flex;
gap: 12px;
align-items: center;
}
.dashboard-select {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
padding: 8px 16px;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
transition: all 0.3s ease;
}
.dashboard-select:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.3);
}
.dashboard-select:focus {
outline: none;
box-shadow: 0 0 0 2px rgba(76, 201, 240, 0.3);
}
.dashboard-button {
background: linear-gradient(135deg, #4361ee 0%, #3a0ca3 100%);
border: none;
color: white;
padding: 8px 16px;
border-radius: 6px;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.3s ease;
}
.dashboard-button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(67, 97, 238, 0.4);
}
.dashboard-button:active {
transform: translateY(0);
}
.dashboard-button.refreshing .refresh-icon {
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.grid-item {
background: rgba(255, 255, 255, 0.05);
border-radius: 10px;
padding: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.grid-item:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
border-color: rgba(76, 201, 240, 0.3);
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.chart-header h3 {
margin: 0;
font-size: 18px;
font-weight: 500;
color: #e0e0e0;
}
.chart-stats {
display: flex;
align-items: center;
gap: 10px;
}
.current-value {
font-size: 24px;
font-weight: 600;
color: #4cc9f0;
}
.change-indicator {
font-size: 14px;
padding: 4px 8px;
border-radius: 4px;
}
.change-indicator.increase {
background: rgba(76, 175, 80, 0.2);
color: #4caf50;
}
.change-indicator.decrease {
background: rgba(244, 67, 54, 0.2);
color: #f44336;
}
.change-indicator.neutral {
background: rgba(158, 158, 158, 0.2);
color: #9e9e9e;
}
.chart-container {
width: 100%;
height: 300px;
}
.dashboard-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
font-size: 14px;
color: #b0b0b0;
}
.last-updated {
display: flex;
align-items: center;
gap: 8px;
}
#lastUpdatedTime {
color: #4cc9f0;
font-weight: 500;
}
.data-source-info {
display: flex;
align-items: center;
gap: 8px;
}
.data-source-info::before {
content: "⏱️";
font-size: 12px;
}
/* 全屏模式样式 */
:fullscreen .real-time-dashboard-container {
padding: 40px;
border-radius: 0;
}
:fullscreen .dashboard-grid {
grid-template-columns: repeat(auto-fit, minmax(600px, 1fr));
gap: 30px;
}
:fullscreen .chart-container {
height: 400px;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.dashboard-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.dashboard-header {
flex-direction: column;
gap: 15px;
align-items: flex-start;
}
.dashboard-controls {
width: 100%;
justify-content: space-between;
}
.dashboard-grid {
grid-template-columns: 1fr;
}
.grid-item {
min-width: 100%;
}
}
/* 加载动画 */
.chart-loading {
position: relative;
}
.chart-loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 40px;
height: 40px;
margin: -20px 0 0 -20px;
border: 3px solid rgba(76, 201, 240, 0.3);
border-top-color: #4cc9f0;
border-radius: 50%;
animation: spin 1s linear infinite;
}
/* 错误提示 */
.dashboard-error {
position: fixed;
top: 20px;
right: 20px;
background: #f44336;
color: white;
padding: 12px 20px;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(244, 67, 54, 0.3);
z-index: 1000;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
第四章:管理后台与配置界面
4.1 创建管理菜单和设置页面
<?php
class RealTime_Dashboard_Admin {
private $settings_page;
public function __construct() {
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'register_settings'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
}
public function add_admin_menu() {
$this->settings_page = add_menu_page(
'实时数据大屏设置',
'数据大屏',
'manage_options',
'real-time-dashboard',
array($this, 'render_settings_page'),
'dashicons-chart-area',
30
);
// 添加子菜单
add_submenu_page(
'real-time-dashboard',
'数据源配置',
'数据源配置',
'manage_options',
'real-time-dashboard-sources',
array($this, 'render_sources_page')
);
add_submenu_page(
'real-time-dashboard',
'图表设置',
'图表设置',
'manage_options',
'real-time-dashboard-charts',
array($this, 'render_charts_page')
);
}
public function render_settings_page() {
?>
<div class="wrap real-time-dashboard-admin">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<div class="dashboard-admin-header">
<div class="admin-stats">
<div class="stat-card">
<h3>数据源状态</h3>
<div class="stat-value" id="dataSourcesStatus">检查中...</div>
</div>
<div class="stat-card">
<h3>最后更新</h3>
<div class="stat-value" id="lastDataUpdate">--:--:--</div>
</div>
<div class="stat-card">
<h3>缓存状态</h3>
<div class="stat-value" id="cacheStatus">正常</div>
</div>
</div>
</div>
<form method="post" action="options.php">
<?php
settings_fields('real_time_dashboard_settings');
do_settings_sections('real_time_dashboard_settings');
submit_button('保存设置');
?>
</form>
<div class="admin-actions">
<h2>快速操作</h2>
<div class="action-buttons">
<button type="button" class="button button-primary" id="clearCacheBtn">
清除缓存
</button>
<button type="button" class="button button-secondary" id="testDataBtn">
测试数据源
</button>
<button type="button" class="button button-secondary" id="exportConfigBtn">
导出配置
</button>
<a href="<?php echo home_url('/real-time-dashboard/'); ?>"
class="button button-secondary" target="_blank">
查看大屏
</a>
</div>
</div>
</div>
<?php
}
public function register_settings() {
// 注册基本设置
register_setting(
'real_time_dashboard_settings',
'rtd_general_settings',
