随时查看等级进度!

大佬们好,我是刚入论坛的丁真
看了不少帖子发现好多新人对于查看等级进度有需求,目前论坛貌似还没有相关功能
hui总发过等级相关的贴 📌 社区权限对照表和升级条件
有需要的话直接扔油猴刷新页面就好啦
不知道填写基本资料这个是怎么满足的,目前是判断个人资料中自我介绍内容
以及黄金等级后,对于判断过去100 天的数据,没找到能获取数据的接口,得依赖数据库统计了,还是等论坛大佬们推出相关功能吧
黄金之前大伙儿还是能凑活用一用的
使用有问题可以用Exia佬的脚本论坛等级查看(自用版)

// ==UserScript==
// @name         MJJBOX 等级进度查看器
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  为 MJJBOX 论坛添加等级进度查看功能,显示当前等级和升级进度
// @author       AI Assistant
// @match        https://mjjbox.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_cookie
// @connect      mjjbox.com
// ==/UserScript==

(function() {
    'use strict';

    // 检查是否在iframe中运行,如果是则不执行
    if (window !== window.top) {
        return;
    }



    // 等级名称映射
    const levelNames = {
        0: '青铜会员',
        1: '白银会员',
        2: '黄金会员',
        3: '钻石会员',
        4: '星曜会员'
    };

    // 等级规则配置
    const levelRequirements = {
        1: { // TL0 → TL1(青铜会员 → 白银会员)
            topics_entered: 5,
            posts_read: 30,
            time_read: 10 * 60 // 10分钟,以秒为单位
        },
        2: { // TL1 → TL2(白银会员 → 黄金会员) 
            days_visited: 15,
            topics_entered: 20,
            posts_read: 100,
            time_read: 60 * 60, // 60分钟
            posts_created: 1,
            likes_received: 1,
            likes_given: 1,
            has_avatar: true,
            has_bio: true
        },
        3: { // TL2 → TL3(黄金会员 → 钻石会员)
            days_visited_in_100: 50, // 过去100天访问50天
            topics_entered: 200,
            posts_read: 500,
            posts_created_in_100: 10, // 过去100天发表10篇回复
            likes_received: 20,
            likes_given: 30,
            flagged_posts_ratio: 0.05 // 被举报帖子比例小于5%
        },
        4: { // TL3 → TL4(钻石会员 → 星曜会员)
            manual_promotion: true // 需要管理员手动提升
        }
    };

    // ===== 样式定义 =====
    const styles = `
        .mjjbox-level-badge {
            position: fixed;
            top: 20px;
            right: 20px;
            width: 60px;
            height: 60px;
            border-radius: 50%;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            font-size: 12px;
            cursor: pointer;
            z-index: 9999;
            box-shadow: 0 4px 15px rgba(0,0,0,0.2);
            transition: all 0.3s ease;
            border: 3px solid white;
        }

        .mjjbox-level-badge:hover {
            transform: scale(1.1);
            box-shadow: 0 6px 25px rgba(0,0,0,0.3);
        }

        .mjjbox-level-badge.level-0 { 
            background: linear-gradient(135deg, #9ca3af 0%, #6b7280 100%); 
        }
        .mjjbox-level-badge.level-1 { 
            background: linear-gradient(135deg, #10b981 0%, #059669 100%); 
        }
        .mjjbox-level-badge.level-2 { 
            background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); 
        }
        .mjjbox-level-badge.level-3 { 
            background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); 
        }
        .mjjbox-level-badge.level-4 { 
            background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); 
        }
        .mjjbox-level-badge.level-5 { 
            background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); 
        }

        .mjjbox-level-modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.5);
            z-index: 10000;
            opacity: 0;
            visibility: hidden;
            transition: all 0.3s ease;
        }

        .mjjbox-level-modal.show {
            opacity: 1;
            visibility: visible;
        }

        .mjjbox-level-modal-content {
            position: absolute;
            background: white;
            border-radius: 12px;
            padding: 24px;
            width: 420px;
            max-height: 500px;
            overflow-y: auto;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
            transform: scale(0.8) translateY(-20px);
            transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
            transform-origin: top right;
        }

        .mjjbox-level-modal.show .mjjbox-level-modal-content {
            transform: scale(1) translateY(0);
        }

        .mjjbox-level-modal-content::before {
            content: '';
            position: absolute;
            top: -8px;
            right: 30px;
            width: 0;
            height: 0;
            border-left: 8px solid transparent;
            border-right: 8px solid transparent;
            border-bottom: 8px solid white;
            filter: drop-shadow(0 -2px 4px rgba(0, 0, 0, 0.1));
        }

        .mjjbox-level-modal-content.arrow-bottom::before {
            top: auto;
            bottom: -8px;
            border-top: 8px solid white;
            border-bottom: none;
            filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
        }

        .mjjbox-level-header {
            text-align: center;
            margin-bottom: 20px;
            border-bottom: 2px solid #f0f0f0;
            padding-bottom: 15px;
        }

        .mjjbox-level-title {
            font-size: 24px;
            font-weight: bold;
            color: #333;
            margin: 0 0 8px 0;
        }

        .mjjbox-level-subtitle {
            color: #666;
            font-size: 14px;
            margin: 0 0 4px 0;
        }

        .mjjbox-level-score {
            color: #ff6b35;
            font-size: 14px;
            font-weight: 500;
            margin: 0 0 8px 0;
        }

        .mjjbox-progress-section {
            margin: 20px 0;
        }

        .mjjbox-progress-item {
            padding: 12px 0;
            border-bottom: 1px solid #f0f0f0;
            position: relative;
        }

        .mjjbox-progress-item:last-child {
            border-bottom: none;
        }

        .mjjbox-progress-label {
            font-weight: 500;
            color: #333;
            margin-bottom: 8px;
            display: block;
        }

        .mjjbox-progress-bar-container {
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .mjjbox-progress-bar {
            flex: 1;
            height: 8px;
            background: #e5e7eb;
            border-radius: 4px;
            overflow: hidden;
            position: relative;
        }

        .mjjbox-progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #10b981 0%, #059669 100%);
            border-radius: 4px;
            transition: width 0.6s ease;
            position: relative;
        }

        .mjjbox-progress-fill.incomplete {
            background: linear-gradient(90deg, #f59e0b 0%, #d97706 100%);
        }

        .mjjbox-progress-required {
            color: #666;
            font-size: 14px;
            font-weight: 500;
            min-width: 40px;
            text-align: right;
        }

        .mjjbox-progress-tooltip {
            position: absolute;
            top: -35px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0, 0, 0, 0.9);
            color: white;
            padding: 6px 10px;
            border-radius: 6px;
            font-size: 12px;
            white-space: nowrap;
            opacity: 0;
            visibility: hidden;
            transition: all 0.2s ease;
            z-index: 1000;
            pointer-events: none;
        }

        .mjjbox-progress-tooltip::after {
            content: '';
            position: absolute;
            top: 100%;
            left: 50%;
            transform: translateX(-50%);
            border: 4px solid transparent;
            border-top-color: rgba(0, 0, 0, 0.9);
        }

        .mjjbox-progress-item:hover .mjjbox-progress-tooltip {
            opacity: 1;
            visibility: visible;
        }

        .mjjbox-close-btn {
            position: absolute;
            top: 10px;
            right: 15px;
            background: none;
            border: none;
            font-size: 24px;
            cursor: pointer;
            color: #999;
            padding: 5px;
        }

        .mjjbox-close-btn:hover {
            color: #333;
        }

        .mjjbox-loading {
            text-align: center;
            padding: 40px;
            color: #666;
        }

        .mjjbox-error {
            text-align: center;
            padding: 40px;
            color: #ef4444;
        }

        @keyframes pulse {
            0%, 100% { transform: scale(1); }
            50% { transform: scale(1.05); }
        }

        .mjjbox-level-badge.loading {
            animation: pulse 2s infinite;
        }
    `;

    // 添加样式到页面
    const styleSheet = document.createElement('style');
    styleSheet.textContent = styles;
    document.head.appendChild(styleSheet);

    // ===== 工具函数 =====
    
    // 获取当前用户名
    function getCurrentUsername() {
        try {
            // 从 Discourse 对象获取用户名
            if (typeof Discourse !== 'undefined' && Discourse.User && Discourse.User.current()) {
                const user = Discourse.User.current();
                if (user && user.username) {
                    return user.username;
                }
            }
            return null;
        } catch (e) {
            console.error('获取用户名失败:', e);
            return null;
        }
    }



    // 显示通知
    function showNotification(message, type = 'info', duration = 3000) {
        const notification = document.createElement('div');
        notification.style.cssText = `
            position: fixed;
            top: 80px;
            right: 20px;
            padding: 12px 16px;
            border-radius: 8px;
            color: white;
            font-weight: 500;
            z-index: 10001;
            opacity: 0;
            transform: translateX(100%);
            transition: all 0.3s ease;
            max-width: 300px;
        `;

        switch (type) {
            case 'success':
                notification.style.background = '#10b981';
                break;
            case 'error':
                notification.style.background = '#ef4444';
                break;
            case 'warning':
                notification.style.background = '#f59e0b';
                break;
            default:
                notification.style.background = '#3b82f6';
        }

        notification.textContent = message;
        document.body.appendChild(notification);

        // 显示动画
        setTimeout(() => {
            notification.style.opacity = '1';
            notification.style.transform = 'translateX(0)';
        }, 100);

        // 自动隐藏
        setTimeout(() => {
            notification.style.opacity = '0';
            notification.style.transform = 'translateX(100%)';
            setTimeout(() => {
                if (notification.parentNode) {
                    notification.parentNode.removeChild(notification);
                }
            }, 300);
        }, duration);
    }

    // ===== 核心功能函数 =====

    // 创建等级徽章
    function createLevelBadge() {
        const badge = document.createElement('div');
        badge.className = 'mjjbox-level-badge loading';
        badge.textContent = 'LV ?';
        badge.title = '点击查看等级进度';
        
        badge.addEventListener('click', (event) => {
            event.stopPropagation();
            showLevelModal(event);
        });
        
        document.body.appendChild(badge);
        return badge;
    }

    // 更新等级徽章
    function updateLevelBadge(level, username = '') {
        const badge = document.querySelector('.mjjbox-level-badge');
        if (!badge) return;

        badge.textContent = `LV ${level}`;
        badge.className = `mjjbox-level-badge level-${level}`;
        badge.title = `${username} - ${levelNames[level] || '未知等级'} (点击查看详情)`;
        

    }

    // 获取用户等级数据
    function fetchUserLevel() {
        const username = getCurrentUsername();
        if (!username) {
            showNotification('❌ 无法获取当前用户名', 'error');
            return;
        }

        // 并行请求两个接口
        let summaryData = null;
        let userData = null;
        let completedRequests = 0;

        const checkComplete = () => {
            completedRequests++;
            if (completedRequests === 2) {
                processUserData(summaryData, userData, username);
            }
        };

        // 使用GM_cookie API获取Cookie
        if (typeof GM_cookie !== 'undefined') {
            GM_cookie.list({domain: 'mjjbox.com'}, (cookies) => {
                let cookieValue = '';
                
                cookies.forEach(cookie => {
                    if (cookie.name === '_forum_session') {
                        cookieValue += `_forum_session=${cookie.value}; `;
                    } else if (cookie.name === '_t') {
                        cookieValue += `_t=${cookie.value}; `;
                    }
                });
                
                if (cookieValue) {
                    makeRequests(cookieValue.trim());
                } else {
                    showNotification('❌ 无法获取登录信息,请确保已登录', 'error');
                }
            });
        } else {
            console.error('❌ GM_cookie API不可用');
            showNotification('❌ Cookie API不可用', 'error');
        }
        
        // 发起请求的函数
        function makeRequests(cookieValue) {

            // 请求 summary.json
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://mjjbox.com/u/${username}/summary.json`,
                timeout: 15000,
                headers: {
                    'Accept': 'application/json, text/javascript, */*; q=0.01',
                    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                    'Cache-Control': 'no-cache',
                    'Pragma': 'no-cache',
                    'Referer': 'https://mjjbox.com/',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    'X-Requested-With': 'XMLHttpRequest',
                    'Cookie': cookieValue
                },
                onload: function(response) {
                    if (response.status === 200) {
                        try {
                            summaryData = JSON.parse(response.responseText);
                        } catch (e) {
                            console.error('解析 summary 数据失败:', e);
                        }
                    } else {
                        console.error(`summary 请求失败: HTTP ${response.status}`);
                    }
                    checkComplete();
                },
                onerror: function(error) {
                    console.error('summary 网络请求失败:', error);
                    checkComplete();
                }
            });

            // 请求 user.json
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://mjjbox.com/u/${username}.json`,
                timeout: 15000,
                headers: {
                    'Accept': 'application/json, text/javascript, */*; q=0.01',
                    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                    'Cache-Control': 'no-cache',
                    'Pragma': 'no-cache',
                    'Referer': 'https://mjjbox.com/',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    'X-Requested-With': 'XMLHttpRequest',
                    'Cookie': cookieValue
                },
                onload: function(response) {
                    if (response.status === 200) {
                        try {
                            userData = JSON.parse(response.responseText);
                        } catch (e) {
                            console.error('解析 user 数据失败:', e);
                        }
                    } else {
                        console.error(`user 请求失败: HTTP ${response.status}`);
                    }
                    checkComplete();
                },
                onerror: function(error) {
                    console.error('user 网络请求失败:', error);
                    checkComplete();
                }
            });
        }
    }

    // 处理用户数据
    function processUserData(summaryData, userData, username) {
        if (!summaryData || !userData) {
            showNotification('❌ 获取用户数据失败', 'error');
            return;
        }

        // 优先使用userData.user,它包含完整的用户信息
        const user = userData.user || summaryData.users[0];
        const userSummary = summaryData.user_summary;



        if (user && user.trust_level !== undefined) {
            const currentLevel = user.trust_level;

            // 更新等级徽章
            updateLevelBadge(currentLevel, username);
            
            // 显示获取成功提示
            showNotification(`✅ 等级信息获取成功: LV${currentLevel} ${levelNames[currentLevel] || '未知等级'}`, 'success', 2000);
            
            // 直接显示弹窗
            const levelData = {
                level: currentLevel,
                username: username,
                userData: {
                    user: user,
                    userSummary: userSummary,
                    gamification_score: user.gamification_score || 0
                }
            };
            
            // 获取徽章位置并显示弹窗
            const badge = document.querySelector('.mjjbox-level-badge');
            const badgeRect = badge.getBoundingClientRect();
            const modal = createLevelModal(levelData, badgeRect);
            document.body.appendChild(modal);
            
            // 显示动画
            setTimeout(() => {
                modal.classList.add('show');
            }, 10);
        } else {
            console.error('用户数据中未找到 trust_level');
            showNotification('❌ 无法获取用户等级信息', 'error');
        }
    }

    // 计算升级进度
    function calculateLevelProgress(currentLevel, userData) {
        if (!userData || !userData.userSummary) {
            return { items: [], achievedCount: 0, totalCount: 0 };
        }

        const userSummary = userData.userSummary;
        const user = userData.user;
        const nextLevel = currentLevel + 1;
        const requirements = levelRequirements[nextLevel];

        if (!requirements) {
            // 已达到最高等级或需要手动提升
            if (currentLevel >= 4) {
                return {
                    items: [{
                        label: '升级方式',
                        current: '联系管理员',
                        required: '手动提升',
                        isMet: false
                    }],
                    achievedCount: 0,
                    totalCount: 1
                };
            }
            return { items: [], achievedCount: 0, totalCount: 0 };
        }

        const items = [];
        let achievedCount = 0;

        // 检查各项要求
        if (requirements.topics_entered !== undefined) {
            const current = userSummary.topics_entered || 0;
            const required = requirements.topics_entered;
            const isMet = current >= required;
            const percentage = Math.min((current / required) * 100, 100);
            items.push({
                label: '阅读主题数',
                current: current,
                required: required,
                isMet: isMet,
                percentage: percentage
            });
            if (isMet) achievedCount++;
        }

        if (requirements.posts_read !== undefined) {
            const current = userSummary.posts_read_count || 0;
            const required = requirements.posts_read;
            const isMet = current >= required;
            const percentage = Math.min((current / required) * 100, 100);
            items.push({
                label: '阅读帖子数',
                current: current,
                required: required,
                isMet: isMet,
                percentage: percentage
            });
            if (isMet) achievedCount++;
        }

        if (requirements.time_read !== undefined) {
            const current = userSummary.time_read || 0;
            const required = requirements.time_read;
            const isMet = current >= required;
            const percentage = Math.min((current / required) * 100, 100);
            items.push({
                label: '阅读时间',
                current: current,
                required: required,
                isMet: isMet,
                percentage: percentage,
                isTime: true
            });
            if (isMet) achievedCount++;
        }

        if (requirements.days_visited !== undefined) {
            const current = userSummary.days_visited || 0;
            const required = requirements.days_visited;
            const isMet = current >= required;
            const percentage = Math.min((current / required) * 100, 100);
            items.push({
                label: '访问天数',
                current: current,
                required: required,
                isMet: isMet,
                percentage: percentage
            });
            if (isMet) achievedCount++;
        }

        if (requirements.posts_created !== undefined) {
            const current = (userSummary && typeof userSummary.topic_count === 'number') ? userSummary.topic_count : 0;
            const required = requirements.posts_created;
            const isMet = current >= required;
            const percentage = Math.min((current / required) * 100, 100);
            items.push({
                label: '发帖数',
                current: current,
                required: required,
                isMet: isMet,
                percentage: percentage
            });
            if (isMet) achievedCount++;
        }

        if (requirements.likes_received !== undefined) {
            const current = userSummary.likes_received || 0;
            const required = requirements.likes_received;
            const isMet = current >= required;
            const percentage = Math.min((current / required) * 100, 100);
            items.push({
                label: '收到赞数',
                current: current,
                required: required,
                isMet: isMet,
                percentage: percentage
            });
            if (isMet) achievedCount++;
        }

        if (requirements.likes_given !== undefined) {
            const current = userSummary.likes_given || 0;
            const required = requirements.likes_given;
            const isMet = current >= required;
            const percentage = Math.min((current / required) * 100, 100);
            items.push({
                label: '给出赞数',
                current: current,
                required: required,
                isMet: isMet,
                percentage: percentage
            });
            if (isMet) achievedCount++;
        }

        if (requirements.has_avatar !== undefined) {
            // 检查是否有自定义头像
            // 如果有custom_avatar_template且不是系统默认头像,则认为已上传
            const hasCustomAvatar = user.custom_avatar_template && 
                                   user.custom_avatar_template.trim() !== '' && 
                                   !user.custom_avatar_template.includes('letter_avatar') &&
                                   !user.custom_avatar_template.includes('system_avatar');
            const isMet = hasCustomAvatar;
            items.push({
                label: '上传头像',
                current: isMet ? '已上传' : '未上传',
                required: '已上传',
                isMet: isMet,
                isBoolean: true
            });
            if (isMet) achievedCount++;
        }

        if (requirements.has_bio !== undefined) {
            // 检查是否有个人简介
            const hasBio = user.bio_raw && user.bio_raw.trim() !== '';
            const isMet = hasBio;
            items.push({
                label: '填写基本资料',
                current: isMet ? '已填写' : '未填写',
                required: '已填写',
                isMet: isMet,
                isBoolean: true
            });
            if (isMet) achievedCount++;
        }

        // 排序:未完成的指标显示在前面
        items.sort((a, b) => {
            if (a.isMet === b.isMet) return 0;
            return a.isMet ? 1 : -1; // 未完成(false)排在前面
        });

        return {
            items: items,
            achievedCount: achievedCount,
            totalCount: items.length
        };
    }

    // 显示等级信息弹窗
    function showLevelModal(event) {
        showNotification('🔍 正在获取等级信息...', 'info');
        fetchUserLevel();
    }

    // 创建等级信息弹窗
    function createLevelModal(levelData, badgeRect) {
        const modal = document.createElement('div');
        modal.className = 'mjjbox-level-modal';

        const progress = calculateLevelProgress(levelData.level, levelData.userData);
        const currentLevelName = levelNames[levelData.level] || '未知等级';
        const nextLevelName = levelNames[levelData.level + 1] || '最高等级';

        // 计算弹窗位置
        const modalContent = document.createElement('div');
        modalContent.className = 'mjjbox-level-modal-content';
        
        // 设置弹窗位置 - 在徽章下方偏左一点
        const top = badgeRect.bottom + 18; // 增加一点距离给箭头
        const right = window.innerWidth - badgeRect.right;
        
        modalContent.style.top = `${top}px`;
        modalContent.style.right = `${right}px`;
        
        // 如果弹窗会超出屏幕底部,则显示在徽章上方
        if (top + 500 > window.innerHeight) {
            modalContent.style.top = `${badgeRect.top - 500 - 18}px`;
            modalContent.classList.add('arrow-bottom'); // 箭头指向下方
        }
        
        // 如果弹窗会超出屏幕左侧,则调整位置
        if (badgeRect.right - 420 < 0) {
            modalContent.style.right = '10px';
        }

        modalContent.innerHTML = `
                <button class="mjjbox-close-btn">&times;</button>
                
                <div class="mjjbox-level-header">
                    <h2 class="mjjbox-level-title">${levelData.username}</h2>
                    <p class="mjjbox-level-subtitle">当前等级: LV${levelData.level} ${currentLevelName}</p>
                    <p class="mjjbox-level-score">当前积分: ${levelData.userData.gamification_score || 0}</p>
                </div>

                <div class="mjjbox-progress-section">
                    <h3 style="margin-bottom: 15px; color: #333;">
                        ${levelData.level >= 4 ? '已达到最高等级' : `升级到 LV${levelData.level + 1} ${nextLevelName} 的进度 (${progress.achievedCount}/${progress.totalCount})`}
                    </h3>
                    
                    ${progress.items.map(item => {
                        if (item.isBoolean) {
                            // 布尔值类型:显示状态图标
                            return `
                            <div class="mjjbox-progress-item">
                                <span class="mjjbox-progress-label">${item.label}</span>
                                <div class="mjjbox-progress-bar-container">
                                    <div class="mjjbox-progress-bar">
                                        <div class="mjjbox-progress-fill ${item.isMet ? '' : 'incomplete'}" 
                                             style="width: ${item.isMet ? 100 : 0}%"></div>
                                    </div>
                                    <span class="mjjbox-progress-required">${item.required}</span>
                                </div>
                                <div class="mjjbox-progress-tooltip">
                                    状态: ${item.current}
                                </div>
                            </div>
                            `;
                        } else {
                            // 数值类型:显示进度条
                            const displayValue = item.isTime ? 
                                `${Math.floor(item.current / 60)}分钟` : 
                                item.current;
                            const displayRequired = item.isTime ? 
                                `${Math.floor(item.required / 60)}分钟` : 
                                item.required;
                            return `
                            <div class="mjjbox-progress-item">
                                <span class="mjjbox-progress-label">${item.label}</span>
                                <div class="mjjbox-progress-bar-container">
                                    <div class="mjjbox-progress-bar">
                                        <div class="mjjbox-progress-fill ${item.isMet ? '' : 'incomplete'}" 
                                             style="width: ${item.percentage}%"></div>
                                    </div>
                                    <span class="mjjbox-progress-required">${displayRequired}</span>
                                </div>
                                <div class="mjjbox-progress-tooltip">
                                    当前: ${displayValue} / 需要: ${displayRequired}
                                </div>
                            </div>
                            `;
                        }
                    }).join('')}
                    
                    ${progress.items.length === 0 ? `
                        <div style="text-align: center; padding: 20px; color: #666;">
                            ${levelData.level >= 4 ? '🎉 恭喜!您已达到最高等级!' : '暂无升级要求信息'}
                        </div>
                    ` : ''}
                </div>


        `;

        // 添加内容到模态框
        modal.appendChild(modalContent);

        // 关闭按钮事件
        const closeBtn = modalContent.querySelector('.mjjbox-close-btn');
        closeBtn.addEventListener('click', () => {
            modal.classList.remove('show');
            setTimeout(() => modal.remove(), 300);
        });



        // 点击背景关闭
        modal.addEventListener('click', (e) => {
            if (e.target === modal) {
                modal.classList.remove('show');
                setTimeout(() => modal.remove(), 300);
            }
        });

        // ESC键关闭
        const handleEsc = (e) => {
            if (e.key === 'Escape') {
                modal.classList.remove('show');
                setTimeout(() => modal.remove(), 300);
                document.removeEventListener('keydown', handleEsc);
            }
        };
        document.addEventListener('keydown', handleEsc);

        return modal;
    }

    // 初始化等级徽章
    function initLevelBadge() {
        // 等级徽章已初始化
    }

    // ===== 初始化 =====
    function init() {
        try {
            // 等待页面加载完成
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', init);
                return;
            }

            // 创建等级徽章
            const badge = createLevelBadge();

            // 初始化等级徽章
            setTimeout(() => {
                initLevelBadge();
            }, 1000);

        } catch (e) {
            console.error('初始化失败:', e);
        }
    }

    // 等待页面加载后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        setTimeout(init, 100);
    }

    // 暴露全局函数用于调试
    window.mjjboxLevelViewer = {
        fetchUserLevel: fetchUserLevel,
        showLevelModal: showLevelModal
    };

})();


功能改动与BUG修复

2025/09/04 01:10 fix: 移除无用逻辑,优化UI显示,优化头像和资料的判断,显示当前积分
2025/09/03 19:20 fix: 发帖数取值错误
2025/09/03 17:20 feat: 支持自定义缓存时间,默认1440分钟

2025/09/03 17:00 fix: 等级显示错误

6 个赞

这是人才啊

1 个赞

这个不错

2 个赞

:grinning_face_with_smiling_eyes: 全靠ai

人才

佬可以试试,我自己看是没问题的,还不清楚有没其他bug呢

6,人才

有没有支持手机的浏览器

欢迎欢迎:clap:

为啥我添加了获取不了

:sweat_smile:

F12控制台有报错嘛佬,获取不到用户?

:open_mouth:不知道诶佬,我得试试,没用过手机的脚本

我是苹果手机

丁真佬

为什么我的发帖数满足,也编辑个人简介,还是不行

访问天数差1啊你没看么

他这个插件显示我的资料不够 ,发帖数量也不够

发帖数是有BUG,我改一下,个人简介那里判断还有问题,我还在调试

1 个赞

我试了雨见浏览器可以添加脚本进去,也能用,但是手机端得做下适配(不过有佬说app不太安全)
其他的via应该也可以,不过得调整脚本了,现在的运行不了

1 个赞

牛啊,大佬,这太方便了,爽