子比主题在线用户小工具:实时掌握网站访客动态-资讯论坛-小武站

子比主题在线用户小工具:实时掌握网站访客动态

在运营网站的过程中,了解实时访客情况是许多站长的需求。近日,一款基于子比主题开发的在线用户小工具应运而生,为站长们提供了便捷查看网站当前活跃用户的功能。

功能亮点

这款小工具的核心功能是展示当前在线用户信息,主要体现在前后端两个方面:

 

  • 前端展示:会清晰显示 “当前有 X 位用户在线” 的信息,并列出在线用户名称。当没有用户在线时,则会显示 “当前没有用户在线” 的提示,直观呈现网站实时访客状态。
  • 后端设置:可在首页主内容上方位置启用该工具,支持自定义标题(默认标题为 “在线用户”),并能设置视为在线的时间(默认 60 秒)。这个时间设置的作用是检测用户活动状态,每 60 秒进行一次检测,当用户超过设定时间未活动时,会被视为离线。

 

值得一提的是,该工具对未登录用户不进行记录,且运行时不会占用过多服务器资源,兼顾了实用性与资源友好性。

安装教程

安装这款小工具的步骤并不复杂:

 

  1. 首先进入网站的文件管理,找到路径 “网站文件 /******/wp-content/themes/zibll/functions.php”;
  2. 将对应代码复制到该文件中;
  3. 进入小工具设置界面启用该功能即可。

 

由于小工具可放置的位置较多,建议在设置时使用实时预览管理,以便选择最合适的展示位置。

/**
Plugin Name: 星空知-在线用户-开始
Plugin URI: https://www.xkzhi.cn/
Author: 空白(留点尊严)
**/

/*星空知-在线用户-开始-www.xkzhi.cn(留下我)*/
// 登录时设置活动状态
function track_user_login($user_login, $user) {
    update_user_meta($user->ID, 'last_activity', time());
    update_user_meta($user->ID, 'is_logged_in', '1');
}
add_action('wp_login', 'track_user_login', 10, 2);

// 每次页面加载时更新活动时间
function update_user_activity() {
    if (is_user_logged_in()) {
        $user_id = get_current_user_id();
        update_user_meta($user_id, 'last_activity', time());
        update_user_meta($user_id, 'is_logged_in', '1');
    }
}
add_action('init', 'update_user_activity');

// 登出时更新状态
function track_user_logout() {
    $user_id = get_current_user_id();
    update_user_meta($user_id, 'is_logged_in', '0');
}
add_action('wp_logout', 'track_user_logout');


class Online_Users_Widget extends WP_Widget {

    // 构造函数
    function __construct() {
        parent::__construct(
            'online_users_widget',
            __('星空知-在线用户', 'text_domain'),
            array(
                'description' => __('显示在线用户列表', 'text_domain'),
                'classname' => 'widget_online_users'
            )
        );
    }

    // 前端显示(带悬浮效果)
    public function widget($args, $instance) {
        // 获取设置
        $title = apply_filters('widget_title', empty($instance['title']) ? __('在线用户', 'text_domain') : $instance['title']);
        $inactive_time = empty($instance['inactive_time']) ? 60 : absint($instance['inactive_time']);
        $show_count = !empty($instance['show_count']);
        
        // 开始输出
        echo $args['before_widget'];
        if (!empty($title)) {
            echo $args['before_title'] . '<span class="online-title-icon">👥</span>' . esc_html($title) . $args['after_title'];
        }
        
        // 获取在线用户
        $online_users = $this->get_online_users($inactive_time);
        
        // 显示用户列表
        if (!empty($online_users)) {
            if ($show_count) {
                echo '<div class="online-users-count">';
                echo '<span class="count-bubble">' . count($online_users) . '</span>';
                printf(
                    _n('当前有 %d 位用户在线', '当前有 %d 位用户在线', count($online_users), 'text_domain'), 
                    count($online_users)
                );
                echo '</div>'; 
            }
            
            echo '<ul class="online-users-list">';
            foreach ($online_users as $user) {
                $display_name = !empty($user->display_name) ? $user->display_name : $user->user_login;
                $avatar = get_avatar($user->ID, 96);
                $user_link = get_author_posts_url($user->ID);
                $last_active = $this->relative_time((int)get_user_meta($user->ID, 'last_activity', true));
                
                echo '<li class="online-user">';
                echo '<a href="' . esc_url($user_link) . '" rel="external nofollow"  class="user-card" title="' . esc_attr($display_name) . '">';
                echo '<div class="avatar-container">';
                echo $avatar;
                echo '<span class="online-status" title="在线"></span>';
                echo '</div>';
                echo '<span class="online-user-name">' . esc_html($display_name) . '</span>';
                
                // 悬浮卡片
                echo '<div class="user-hover-card">';
                echo '<div class="hover-avatar">' . get_avatar($user->ID, 120) . '</div>';
                echo '<h4>' . esc_html($display_name) . '</h4>';
                echo '<p><i class="activity-icon"></i> ' . esc_html($last_active) . '</p>';
                echo '</div>';
                
                echo '</a>';
                echo '</li>';
            }
            echo '</ul>';
        } else {
            echo '<div class="no-online-users">';
            echo '<div class="empty-state">';
            echo '<span class="empty-icon">👻</span>';
            echo '<p>' . __('当前没有用户在线', 'text_domain') . '</p>';
            echo '</div>';
            echo '</div>';
        }
        
        echo $args['after_widget'];
    }
// 相对时间显示
    private function relative_time($timestamp) {
        $current_time = time();
        $diff = $current_time - $timestamp;
        
        if ($diff < 60) {
            return __('刚刚在线', 'text_domain');
        } elseif ($diff < 3600) {
            return sprintf(__('%d分钟前', 'text_domain'), floor($diff/60));
        } elseif ($diff < 86400) {
            return sprintf(__('%d小时前', 'text_domain'), floor($diff/3600));
        } else {
            return date(__('Y年m月d日', 'text_domain'), $timestamp);
        }
    }

    // 后台表单
    public function form($instance) {
        $defaults = array(
            'title' => __('在线用户', 'text_domain'),
            'inactive_time' => 60,
            'show_count' => true
        );
        $instance = wp_parse_args((array) $instance, $defaults);
        ?>
        <p>
            <label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('标题:'); ?></label>
            <input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" 
                   name="<?php echo $this->get_field_name('title'); ?>" 
                   type="text" value="<?php echo esc_attr($instance['title']); ?>" />
        </p>
        <p>
            <label for="<?php echo $this->get_field_id('inactive_time'); ?>"><?php _e('视为在线的时间(秒):'); ?></label>
            <input class="widefat" id="<?php echo $this->get_field_id('inactive_time'); ?>" 
                   name="<?php echo $this->get_field_name('inactive_time'); ?>" 
                   type="number" min="60" value="<?php echo esc_attr($instance['inactive_time']); ?>" />
            <small><?php _e('默认60秒'); ?></small>
        </p>
        <p>
            <input class="checkbox" type="checkbox" id="<?php echo $this->get_field_id('show_count'); ?>" 
                   name="<?php echo $this->get_field_name('show_count'); ?>" 
                   <?php checked($instance['show_count'], true); ?> />
            <label for="<?php echo $this->get_field_id('show_count'); ?>"><?php _e('显示在线人数'); ?></label>
        </p>
        <?php
    }

    // 更新设置
    public function update($new_instance, $old_instance) {
        $instance = $old_instance;
        $instance['title'] = sanitize_text_field($new_instance['title']);
        $instance['inactive_time'] = absint($new_instance['inactive_time']);
        $instance['show_count'] = !empty($new_instance['show_count']);
        return $instance;
    }

    // 获取在线用户列表
private function get_online_users($inactive_time = 60) {
        $current_time = time();
        $online_users = array();
        
        $users = get_users(array(
            'meta_query' => array(
                array(
                    'key' => 'last_activity',
                    'compare' => 'EXISTS'
                )
            )
        ));
        
        foreach ($users as $user) {
            $last_activity = (int)get_user_meta($user->ID, 'last_activity', true);
            $is_logged_in = get_user_meta($user->ID, 'is_logged_in', true);
            
            if (($current_time - $last_activity <= $inactive_time) && $is_logged_in === '1') {
                $online_users[$user->ID] = $user;
            }
        }
        
        return $online_users;
    }
}

// 注册小工具
function register_online_users_widget() {
    register_widget('Online_Users_Widget');
}
add_action('widgets_init', 'register_online_users_widget');

//初始化用户状态

function initialize_user_status() {
    if (!get_option('online_users_status_initialized')) {
        $users = get_users();
        foreach ($users as $user) {
            if (!metadata_exists('user', $user->ID, 'is_logged_in')) {
                update_user_meta($user->ID, 'is_logged_in', '0');
            }
            if (!metadata_exists('user', $user->ID, 'last_activity')) {
                update_user_meta($user->ID, 'last_activity', time());
            }
        }
        update_option('online_users_status_initialized', true);
    }
}
add_action('admin_init', 'initialize_user_status');

// CSS

function online_users_widget_styles() {
    ?>
    <style>
    /* 基础样式 - 优化阴影 */
    .widget_online_users {
        background: linear-gradient(135deg, #f5f7fa 0%, #f8fafc 100%);
        border-radius: 16px;
        box-shadow: 
            0 1px 1px rgba(0,0,0,0.02),
            0 2px 2px rgba(0,0,0,0.02),
            0 4px 4px rgba(0,0,0,0.02),
            0 8px 8px rgba(0,0,0,0.03),
            0 16px 16px rgba(0,0,0,0.03);
        padding: 25px;
        margin-bottom: 30px;
        border: 1px solid rgba(255,255,255,0.5);
        backdrop-filter: blur(8px);
        transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
    }
    
    .widget_online_users:hover {
        box-shadow: 
            0 1px 1px rgba(0,0,0,0.03),
            0 2px 2px rgba(0,0,0,0.03),
            0 4px 4px rgba(0,0,0,0.03),
            0 8px 8px rgba(0,0,0,0.04),
            0 16px 16px rgba(0,0,0,0.04),
            0 24px 24px rgba(0,0,0,0.05);
        transform: translateY(-3px) scale(1.005);
    }
    
    /* 标题样式 */
    .widget_online_users .widget-title {
        font-size: 1.3em;
        color: #2d3748;
        margin-bottom: 20px;
        padding-bottom: 12px;
        border-bottom: 1px solid rgba(0,0,0,0.05);
        display: flex;
        align-items: center;
        font-weight: 600;
    }
    
    .online-title-icon {
        margin-right: 10px;
        font-size: 1.2em;
        filter: drop-shadow(0 1px 1px rgba(0,0,0,0.1));
    }
    
    /* 在线人数统计 */
    .widget_online_users .online-users-count {
        background: rgba(74, 85, 104, 0.1);
        padding: 8px 15px;
        border-radius: 50px;
        font-size: 0.9em;
        color: #4a5568;
        margin-bottom: 20px;
        display: inline-flex;
        align-items: center;
        transition: all 0.3s ease;
    }
    .widget_online_users .online-users-count:hover {
        background: rgba(74, 85, 104, 0.2);
    }
    
    .count-bubble {
        background: linear-gradient(135deg, #4CAF50 0%, #81C784 100%);
        color: white;
        width: 24px;
        height: 24px;
        border-radius: 50%;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        margin-right: 8px;
        font-size: 0.8em;
        font-weight: bold;
        box-shadow: 
            0 1px 1px rgba(76, 175, 80, 0.2),
            0 2px 2px rgba(76, 175, 80, 0.15),
            0 4px 4px rgba(76, 175, 80, 0.1);
    }
    
    /* 用户列表 */
    .widget_online_users .online-users-list {
        list-style: none;
        padding: 0;
        margin: 0 -5px;
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
    }
    
    /* 单个用户项 */
    .widget_online_users .online-user {
        display: flex;
        flex-direction: column;
        align-items: center;
        width: 80px;
        margin: 0 5px 15px;
        position: relative;
    }
    
    .widget_online_users .user-card {
        display: flex;
        flex-direction: column;
        align-items: center;
        text-decoration: none;
        transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
        position: relative;
        will-change: transform;
    }
    
    .widget_online_users .user-card:hover {
        transform: translateY(-5px);
    }
    
    /* 头像容器 */
    .widget_online_users .avatar-container {
        position: relative;
        margin-bottom: 8px;
        transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
        will-change: transform;
    }
    
    .widget_online_users .user-card:hover .avatar-container {
        transform: scale(1.05);
    }
    
    /* 用户头像 */
    .widget_online_users .online-user img.avatar {
        width: 60px;
        height: 60px;
        border-radius: 50%;
        object-fit: cover;
        border: 3px solid white;
        box-shadow: 
            0 1px 1px rgba(0,0,0,0.05),
            0 2px 2px rgba(0,0,0,0.05),
            0 4px 4px rgba(0,0,0,0.05),
            0 6px 6px rgba(0,0,0,0.06);
        transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1);
    }
    
    .widget_online_users .user-card:hover img.avatar {
        box-shadow: 
            0 2px 2px rgba(0,0,0,0.06),
            0 4px 4px rgba(0,0,0,0.06),
            0 8px 8px rgba(0,0,0,0.07),
            0 12px 12px rgba(0,0,0,0.08);
    }
    
    /* 在线状态指示器 */
    .widget_online_users .online-status {
        position: absolute;
        bottom: 5px;
        right: 5px;
        width: 14px;
        height: 14px;
        background: linear-gradient(135deg, #4CAF50 0%, #81C784 100%);
        border-radius: 50%;
        border: 2px solid white;
        box-shadow: 
            0 0 0 1px rgba(76, 175, 80, 0.2),
            0 0 0 2px rgba(76, 175, 80, 0.15);
        animation: pulse 1.5s infinite;
        z-index: 2;
    }
/* 用户名 */
    .widget_online_users .online-user-name {
        font-size: 0.8em;
        color: #4a5568;
        text-align: center;
        word-break: break-word;
        line-height: 1.4;
        font-weight: 500;
        transition: all 0.3s ease;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        display: -webkit-box;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
    }
    
    .widget_online_users .user-card:hover .online-user-name {
        color: #2d3748;
        font-weight: 600;
    }
    
.widget_online_users .user-hover-card {
    position: absolute;
    bottom: calc(100% + 10px);
    left: 50%;
    transform: translateX(-50%) translateY(10px);
    width: 180px;
    background: white;
    border-radius: 12px;
    padding: 15px;
    opacity: 0;
    visibility: hidden;
    transition: 
        opacity 0.3s ease-out,
        transform 0.4s cubic-bezier(0.23, 1, 0.32, 1);
    z-index: 10;
    pointer-events: none;
    text-align: center;
    /* 已移除所有阴影相关属性 */
    box-shadow: none;
    filter: none;
}

.widget_online_users .user-card:hover .user-hover-card {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
    /* 已移除悬浮状态的阴影增强 */
    box-shadow: none;
    filter: none;
}
    
    /* 添加卡片升起时的微光效果 */
    .widget_online_users .user-hover-card::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        height: 40%;
        background: linear-gradient(to bottom, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0) 100%);
        border-radius: 12px 12px 0 0;
        opacity: 0;
        transition: opacity 0.4s ease;
    }
    
    .widget_online_users .user-card:hover .user-hover-card::before {
        opacity: 0.6;
    }
    
    /* 优化卡片内容过渡 */
    .widget_online_users .user-hover-card > * {
        transform: translateY(5px);
        opacity: 0.9;
        transition: 
            transform 0.4s cubic-bezier(0.23, 1, 0.32, 1),
            opacity 0.4s ease;
    }
    
    .widget_online_users .user-card:hover .user-hover-card > * {
        transform: translateY(0);
        opacity: 1;
    }
    
    /* 延迟子元素动画 */
    .widget_online_users .user-hover-card .hover-avatar {
        transition-delay: 0.05s;
    }
    
    .widget_online_users .user-hover-card h4 {
        transition-delay: 0.1s;
    }
    
    .widget_online_users .user-hover-card p {
        transition-delay: 0.15s;
    }
    
    .activity-icon {
        display: inline-block;
        width: 12px;
        height: 12px;
        background: #4CAF50;
        border-radius: 50%;
        margin-right: 5px;
        animation: pulse 1.5s infinite;
    }
    
    /* 无用户状态 */
    .widget_online_users .no-online-users {
        padding: 20px 0;
    }
.widget_online_users .empty-state {
        display: flex;
        flex-direction: column;
        align-items: center;
        text-align: center;
    }
    
    .widget_online_users .empty-icon {
        font-size: 2em;
        opacity: 0.5;
        margin-bottom: 10px;
        filter: drop-shadow(0 1px 1px rgba(0,0,0,0.1));
    }
    
    .widget_online_users .empty-state p {
        margin: 0;
        color: #718096;
        font-size: 0.9em;
    }
    
    @keyframes pulse {
        0% { 
            transform: scale(0.95); 
            box-shadow: 
                0 0 0 0 rgba(76, 175, 80, 0.4),
                0 0 0 1px rgba(76, 175, 80, 0.3);
        }
        70% { 
            transform: scale(1.05); 
            box-shadow: 
                0 0 0 4px rgba(76, 175, 80, 0.1),
                0 0 0 6px rgba(76, 175, 80, 0);
        }
        100% { 
            transform: scale(0.95); 
            box-shadow: 
                0 0 0 0 rgba(76, 175, 80, 0),
                0 0 0 1px rgba(76, 175, 80, 0.1);
        }
    }
    
    @media (max-width: 480px) {
        .widget_online_users {
            padding: 20px 15px;
            border-radius: 12px;
        }
        
        .widget_online_users .online-user {
            width: 70px;
        }
        
        .widget_online_users .online-user img.avatar {
            width: 50px;
            height: 50px;
        }
        
        .widget_online_users .user-hover-card {
            width: 160px;
            padding: 12px;
        }
    }
    </style>
    <?php
}
add_action('wp_head', 'online_users_widget_styles');
/*星空知-在线用户-结束-www.xkzhi.cn(留下我)*/

 

 

这款在线用户小工具是子比主题的一次实用二次开发,为站长实时了解网站动态提供了新的途径,对于提升网站运营效率有一定帮助。如果你正在使用子比主题,不妨尝试一下这款工具,或许能为你的网站管理带来便利。