阿里云百炼模型到期时间提取器

精准提取模型名称、Code、免费额度(支持百分比/无额度)、倒计时、到期时间,一键复制 Code。

安裝腳本?
作者推薦腳本

您可能也會喜歡 Reddit 中文翻譯檢測器

安裝腳本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         阿里云百炼模型到期时间提取器
// @name:en      Bailian Model Expiry Extractor
// @name:zh      阿里云百炼模型到期时间提取器
// @namespace    https://greasyfork.org/zh-CN/scripts/543956-%E9%98%BF%E9%87%8C%E4%BA%91%E7%99%BE%E7%82%BC%E6%A8%A1%E5%9E%8B%E5%88%B0%E6%9C%9F%E6%97%B6%E9%97%B4%E6%8F%90%E5%8F%96%E5%99%A8
// @version      1.5.3
// @author       will
// @description  精准提取模型名称、Code、免费额度(支持百分比/无额度)、倒计时、到期时间,一键复制 Code。
// @description:en Accurately extract model name, code, quota (%, 0, or N/M), countdown, expiry, and copy code.
// @license      MIT
// @homepage     https://github.com/jwq2011/TamperMonkey-Scripts
// @supportURL   https://github.com/jwq2011/TamperMonkey-Scripts/issues
// @match        https://bailian.console.aliyun.com/console*
// @grant        GM_setClipboard
// @run-at       document-start
// @compatible   tampermonkey
// @compatible   violentmonkey
// ==/UserScript==

(function () {
    'use strict';

    const DEBUG = false;
    const LOG_PREFIX = '[Bailian Expiry+]';

    function log(...args) {
        if (DEBUG) console.log(LOG_PREFIX, ...args);
    }

    let extractedData = [];

    // 用户可自定义显示哪些列(默认只显示原始5个)
    const userSettings = {
        showModelType: false,     // 模型类型
        showContextLength: false, // 上下文长度
        showPrice: false,          // 价格
        showProtocol: false,      // 模型协议
        showLimit: false,         // 限流
        showDescription: false,    // 描述
        showVendor: false,        // 供应商(子页面无)
        showUpdateTime: false,     // 更新时间(子页面无)
    };

    (function loadUserSettings() {
        try {
            const saved = localStorage.getItem('bailian_user_settings');
            if (saved) {
                Object.assign(userSettings, JSON.parse(saved));
            }
        } catch (e) {
            console.error('[Bailian Settings] 加载用户设置失败:', e);
        }
    })();

    // 等待页面完全加载
    function waitForPageReady() {
        return new Promise((resolve) => {
            // 如果页面已经加载完成,直接返回
            if (document.readyState === 'complete') {
                resolve();
                return;
            }

            // 等待页面加载完成
            const checkInterval = setInterval(() => {
                if (document.readyState === 'complete') {
                    clearInterval(checkInterval);
                    resolve();
                }
            }, 50); // 更快的检查频率

            // 超时处理
            setTimeout(() => {
                clearInterval(checkInterval);
                resolve();
            }, 2000);
        });
    }

    // 等待表格出现 - 优化版本
    function waitForTable(maxWaitTime = 3000) {
        return new Promise((resolve) => {
            const startTime = Date.now();

            function check() {
                const table = document.querySelector('.efm_ant-table');
                if (table) {
                    log('✅ 表格已加载');
                    resolve({ success: true, table });
                    return;
                }

                if (Date.now() - startTime > maxWaitTime) {
                    log('⚠️ 等待表格超时');
                    resolve({ success: false, table: null });
                    return;
                }

                // 更小的检查间隔
                setTimeout(check, 50);
            }

            check();
        });
    }

    // 等待特定元素出现 - 优化版本
    function waitForElement(selector, maxWaitTime = 2000) {
        return new Promise((resolve) => {
            const startTime = Date.now();

            function check() {
                const element = document.querySelector(selector);
                if (element) {
                    resolve(element);
                    return;
                }

                if (Date.now() - startTime > maxWaitTime) {
                    resolve(null);
                    return;
                }

                setTimeout(check, 50);
            }

            check();
        });
    }

    // 等待并检测列表视图按钮
    async function waitForListViewButton() {
        // 等待一段时间让页面完全渲染
        await new Promise(resolve => setTimeout(resolve, 1000));

        // 查找所有可能的列表视图按钮
        const listViewIcons = document.querySelectorAll('.bl-icon-list-line');
        log(`找到 ${listViewIcons.length} 个列表视图图标`);

        for (let i = 0; i < listViewIcons.length; i++) {
            const icon = listViewIcons[i];
            const button = icon.closest('button');

            // 检查按钮是否可见且可点击
            if (button && button.offsetWidth > 0 && button.offsetHeight > 0) {
                log(`按钮 ${i+1} 可见,正在检查是否已经是列表视图...`);
                // 检查是否有active类
                if (button.classList.contains('active__VRFfX')) {
                    log(`✅ 按钮 ${i+1} 已经是激活状态`);
                    return { success: true, button: null, alreadyActive: true }; // 已经是列表视图
                } else {
                    log(`✅ 找到可点击的列表视图按钮 ${i+1}`);
                    return { success: true, button, alreadyActive: false };
                }
            } else {
                log(`按钮 ${i+1} 不可见或无效`);
            }
        }

        log('⚠️ 未找到可点击的列表视图按钮');
        return { success: false, button: null, alreadyActive: false };
    }

    function createFloatingButton() {
        const btnId = 'bailian-extractor-btn';
        if (document.getElementById(btnId)) return;

        const button = document.createElement('button');
        button.id = btnId;
        Object.assign(button.style, {
            position: 'fixed', top: '80px', right: '20px', zIndex: '2147483647',
            backgroundColor: '#ff6a00', color: 'white', border: 'none',
            padding: '12px 16px', borderRadius: '8px', cursor: 'pointer',
            fontSize: '14px', fontWeight: 'bold', boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
            opacity: 0.95, fontFamily: 'Arial, sans-serif'
        });
        button.textContent = '📊 提取模型信息';

        // 自动切换到列表视图(精准判断)- 优化版本
        async function switchToListView() {
            log('🔍 正在尝试切换到列表视图...');

            // 快速检查当前视图状态
            const currentViewIcon = document.querySelector('.bl-icon-list-line.active__VRFfX');
            if (currentViewIcon) {
                log('✅ 当前已是列表视图');
                return false;
            }

            // 使用更高效的等待方式
            await new Promise(resolve => setTimeout(resolve, 100));

            // 查找所有列表视图图标
            const listViewIcons = document.querySelectorAll('.bl-icon-list-line');
            log(`找到 ${listViewIcons.length} 个列表视图图标`);

            if (listViewIcons.length === 0) {
                log('⚠️ 未找到列表视图图标');
                return false;
            }

            // 遍历所有图标,找到可见的并尝试点击
            for (let i = 0; i < listViewIcons.length; i++) {
                const icon = listViewIcons[i];

                // 更快的可见性检测
                const rect = icon.getBoundingClientRect();
                if (rect.width > 0 && rect.height > 0) {
                    log(`找到可见的列表视图图标 ${i+1}`);

                    // 检查是否已经是激活状态
                    if (!icon.classList.contains('active__VRFfX')) {
                        try {
                            log('正在点击列表视图图标...');
                            icon.click();
                            log('✅ 已点击切换到列表视图');
                            // 极短等待时间
                            await new Promise(resolve => setTimeout(resolve, 150));
                            return true;
                        } catch (error) {
                            log(`点击图标 ${i+1} 失败:`, error);
                        }
                    } else {
                        log(`图标 ${i+1} 已经激活`);
                        return false;
                    }
                } else {
                    log(`图标 ${i+1} 不可见`);
                }
            }

            log('⚠️ 未找到可点击的列表视图图标');
            return false;
        }

        // 修改按钮点击事件处理函数
        button.addEventListener('click', async () => {
            button.disabled = true;
            button.textContent = '🔍 提取中...';

            try {
                // 等待页面加载 - 更快的等待
                await waitForPageReady();

                // 等待表格出现 - 更快的超时
                const tableResult = await waitForTable(2000);

                // 自动切换视图
                let needWait = false;
                if (await switchToListView()) {
                    needWait = true;
                }

                // 自动展开折叠区域
                if (await autoExpandFoldedRows()) {
                    needWait = true;
                }

                // 等待 DOM 更新
                if (needWait) {
                    await new Promise(resolve => setTimeout(resolve, 150)); // 极短等待
                }

                const data = extractAllModels();
                if (data.length === 0) {
                    alert('❌ 未找到任何模型信息,请确认已打开【模型广场】页面并完全加载。');
                } else {
                    extractedData = data;
                    showResultsModal();
                }
            } catch (error) {
                console.error('执行过程中发生错误:', error);
                alert('❌ 执行过程中发生错误,请刷新页面后重试。');
            } finally {
                button.disabled = false;
                button.textContent = '📊 提取模型信息';
            }
        });

        document.body.appendChild(button);

        createSettingsPanel(); // 添加设置按钮

        log('✅ 按钮已创建');
    }

    // 自动展开折叠区域 - 优化版本
    async function autoExpandFoldedRows() {
        log('🔍 正在尝试展开折叠区域...');

        // 极短等待时间
        await new Promise(resolve => setTimeout(resolve, 100));

        let clicked = false;
        let expandedCount = 0;

        // 方法1: 查找所有展开/收起按钮
        const expandButtons = [...document.querySelectorAll('button.efm_ant-table-row-expand-icon')];
        log(`找到 ${expandButtons.length} 个展开/收起按钮`);

        for (const btn of expandButtons) {
            // 更快的可见性检测
            const rect = btn.getBoundingClientRect();
            if (rect.width > 0 && rect.height > 0) {
                // 检查是否为折叠状态
                const isCollapsed = btn.classList.contains('efm_ant-table-row-expand-icon-collapsed');
                const isExpanded = btn.classList.contains('efm_ant-table-row-expand-icon-expanded');

                if (isCollapsed) {
                    try {
                        btn.click();
                        log('✅ 点击展开按钮');
                        expandedCount++;
                        clicked = true;
                        // 极短等待
                        await new Promise(resolve => setTimeout(resolve, 50));
                    } catch (error) {
                        log('点击展开按钮失败:', error);
                    }
                } else if (isExpanded) {
                    log('✅ 按钮已是展开状态');
                }
            }
        }

        if (expandedCount > 0) {
            log(`✅ 成功展开 ${expandedCount} 个折叠项`);
        } else {
            log('⚠️ 未找到可展开的折叠项');
        }

        return clicked;
    }

    // 提取模型信息
    function extractAllModels() {
        log('🔍 开始提取模型数据...');

        // 等待表格出现
        const maxWaitTime = 5000;
        const startTime = Date.now();

        while (Date.now() - startTime < maxWaitTime) {
            const table = document.querySelector('.efm_ant-table');
            if (table) {
                log('✅ 表格已加载');
                break;
            }
            // 短暂等待
            const dummy = new Promise(resolve => setTimeout(resolve, 100));
            dummy.then(() => {});
        }

        // 查找行元素
        const rowSelectors = [
            'tr[data-row-key]',
            '.ant-table-row',
            'tr[role="row"]',
            '.efm_ant-table-row'
        ];

        let rows = [];
        for (const sel of rowSelectors) {
            rows = [...document.querySelectorAll(sel)];
            if (rows.length > 0) {
                log(`✅ 找到 ${rows.length} 行数据`);
                break;
            }
        }

        if (rows.length === 0) {
            log('❌ 未找到任何行');
            return [];
        }

        const results = [];

        // 判断是否是子页面(详情页)
        const isSubPage = /\/model-market\/detail\//.test(location.hash);

        for (const row of rows) {
            // --- 模型名称 ---
            let name = '未知模型';
            if (isSubPage) {
                const nameEl = row.querySelector('.name__QVnRn') || row.querySelector('td:first-child');
                name = (nameEl?.textContent || '未知模型').trim();
            } else {
                const nameContainer = row.querySelector('.model-name__xEkXf');
                const nameEl = nameContainer?.querySelector('span');
                name = (nameEl?.textContent || '未知模型').trim();
            }

            // --- Code 提取 ---
            let code = '';
            if (isSubPage) {
                const spans = row.querySelectorAll('span');
                for (const span of spans) {
                    const text = span.textContent.trim();
                    if (/^qwen[-\w]*\d/.test(text)) {
                        code = text.toLowerCase();
                        break;
                    }
                }
            } else {
                const codeCell = row.querySelector('td:nth-child(2)');
                const codeText = codeCell?.textContent.trim().split(/\s+/)[0] || '';
                if (/^qwen[-\w]*\d/.test(codeText)) {
                    code = codeText.toLowerCase();
                }
            }
            code = code || '—';

            // --- 免费额度 + 百分比 ---
            let freeQuota = '—';
            let quotaText = '0';
            let percentText = '0%';

            const quotaSpan = row.querySelector('.value__V7Z7e');
            if (quotaSpan) {
                const text = quotaSpan.textContent.trim();
                const match = text.match(/(\d[\d,]*)\s*\/\s*(\d[\d,]+)/);
                if (match) {
                    const used = parseInt(match[1].replace(/,/g, ''));
                    const total = parseInt(match[2].replace(/,/g, ''));
                    quotaText = `${used.toLocaleString()}/${total.toLocaleString()}`;
                }
            }

            const percentSpan = row.querySelector('.efm_ant-progress-text');
            if (percentSpan) {
                const pct = percentSpan.textContent.trim();
                if (/^\d+(\.\d+)?%$/.test(pct)) {
                    percentText = pct;
                }
            }

            if (quotaText !== '0') {
                freeQuota = `${quotaText} · ${percentText}`;
            } else if (/^\d+(\.\d+)?%$/.test(percentText)) {
                freeQuota = percentText;
            } else {
                freeQuota = /无免费额度/.test(row.textContent) ? '0 · 0%' : '—';
            }

            // --- 到期时间 ---
            const expiryMatch = row.textContent.match(/到期时间.?(\d{4}-\d{2}-\d{2})/);
            if (!expiryMatch) continue;

            const expiry = expiryMatch[1];
            const expiryDate = new Date(expiry);
            const today = new Date().setHours(0, 0, 0, 0);
            const daysLeft = Math.ceil((expiryDate - today) / 86400000);
            if (daysLeft < 0) continue;

            // --- 可选字段提取 ---
            const modelType = userSettings.showModelType ? (row.querySelector('td:nth-child(3)')?.textContent || '—') : undefined;
            const contextLength = userSettings.showContextLength ? (row.querySelector('td:nth-child(4)')?.textContent || '—') : undefined;
            const price = userSettings.showPrice ? (row.querySelector('td:nth-child(5)')?.textContent || '—') : undefined;
            const protocol = userSettings.showProtocol ? (row.querySelector('td:nth-child(6)')?.textContent || '—') : undefined;
            const limit = userSettings.showLimit ? (row.querySelector('td:nth-child(9)')?.textContent || '—') : undefined;
            const description = userSettings.showDescription ? (row.querySelector('td:nth-child(10)')?.textContent || '—') : undefined;
            const vendor = (userSettings.showVendor && !isSubPage) ? (row.querySelector('td:nth-child(11)')?.textContent || '—') : undefined;
            const updateTime = (userSettings.showUpdateTime && !isSubPage) ? (row.querySelector('td:nth-child(12)')?.textContent || '—') : undefined;

            results.push({
                name, code, freeQuota, daysLeft, expiry,
                ...(userSettings.showModelType && { modelType }),
                ...(userSettings.showContextLength && { contextLength }),
                ...(userSettings.showPrice && { price }),
                ...(userSettings.showProtocol && { protocol }),
                ...(userSettings.showLimit && { limit }),
                ...(userSettings.showDescription && { description }),
                ...(userSettings.showVendor && { vendor }),
                ...(userSettings.showUpdateTime && { updateTime })
            });

            log('✅ 提取:', name, code, freeQuota, `剩余 ${daysLeft} 天`, expiry);
        }

        return results.sort((a, b) => a.daysLeft - b.daysLeft);
    }

    // 显示结果弹窗
    function showResultsModal() {
        const modalId = 'bailian-extractor-modal';
        if (document.getElementById(modalId)) document.body.removeChild(document.getElementById(modalId));

        const modal = document.createElement('div');
        modal.id = modalId;
        Object.assign(modal.style, {
            position: 'fixed', top: 0, left: 0, width: '100%', height: '100%',
            backgroundColor: 'rgba(0,0,0,0.6)', display: 'flex', alignItems: 'center',
            justifyContent: 'center', zIndex: '2147483647', fontFamily: 'Arial, sans-serif'
        });

        const content = document.createElement('div');
        Object.assign(content.style, {
            backgroundColor: 'white', width: '95%', maxWidth: '1200px', maxHeight: '85vh',
            overflow: 'auto', borderRadius: '10px', padding: '20px', position: 'relative'
        });

        const title = document.createElement('h3');
        title.textContent = `✅ 提取结果(${extractedData.length} 个模型)`;
        content.appendChild(title);

        if (extractedData.length === 0) {
            content.appendChild(document.createTextNode('未找到有效模型信息。'));
        } else {
            const table = document.createElement('table');
            table.style.width = '100%';
            table.style.borderCollapse = 'collapse';
            table.innerHTML = `
                <thead>
                    <tr style="background:#f5f5f5;">
                        <th style="text-align:left;padding:10px;border:1px solid #ddd;">模型名称</th>
                        <th style="text-align:left;padding:10px;border:1px solid #ddd;">Code(点击自动复制)</th>
                        <th style="text-align:left;padding:10px;border:1px solid #ddd;">免费额度</th>
                        <th style="text-align:left;padding:10px;border:1px solid #ddd;">倒计时显示</th>
                        <th style="text-align:left;padding:10px;border:1px solid #ddd;">到期时间</th>
                        ${userSettings.showModelType ? '<th style="text-align:left;padding:10px;border:1px solid #ddd;">模型类型</th>' : ''}
                        ${userSettings.showContextLength ? '<th style="text-align:left;padding:10px;border:1px solid #ddd;">上下文长度</th>' : ''}
                        ${userSettings.showPrice ? '<th style="text-align:left;padding:10px;border:1px solid #ddd;">价格</th>' : ''}
                        ${userSettings.showProtocol ? '<th style="text-align:left;padding:10px;border:1px solid #ddd;">模型协议</th>' : ''}
                        ${userSettings.showLimit ? '<th style="text-align:left;padding:10px;border:1px solid #ddd;">限流</th>' : ''}
                        ${userSettings.showDescription ? '<th style="text-align:left;padding:10px;border:1px solid #ddd;">描述</th>' : ''}
                        ${userSettings.showVendor ? '<th style="text-align:left;padding:10px;border:1px solid #ddd;">供应商</th>' : ''}
                        ${userSettings.showUpdateTime ? '<th style="text-align:left;padding:10px;border:1px solid #ddd;">更新时间</th>' : ''}
                    </tr>
                </thead>
                <tbody></tbody>
            `;

            const tbody = table.querySelector('tbody');
            extractedData.forEach(item => {
                const tr = document.createElement('tr');
                appendCell(tr, item.name);
                appendCodeCell(tr, item.code);
                appendCell(tr, item.freeQuota);
                appendCountdownCell(tr, item.daysLeft);
                appendCell(tr, item.expiry, { color: '#d9534f', fontWeight: 'bold' });
                if (userSettings.showModelType) appendCell(tr, item.modelType || '—');
                if (userSettings.showContextLength) appendCell(tr, item.contextLength || '—');
                if (userSettings.showPrice) appendCell(tr, item.price || '—');
                if (userSettings.showProtocol) appendCell(tr, item.protocol || '—');
                if (userSettings.showLimit) appendCell(tr, item.limit || '—');
                if (userSettings.showDescription) appendCell(tr, item.description || '—');
                if (userSettings.showVendor) appendCell(tr, item.vendor || '—');
                if (userSettings.showUpdateTime) appendCell(tr, item.updateTime || '—');
                tbody.appendChild(tr);
            });

            content.appendChild(table);

            const csvBtn = document.createElement('button');
            csvBtn.textContent = '📋 复制为 CSV';
            csvBtn.style.marginTop = '15px';
            csvBtn.style.padding = '10px';
            csvBtn.style.backgroundColor = '#007cba';
            csvBtn.style.color = 'white';
            csvBtn.style.border = 'none';
            csvBtn.style.borderRadius = '4px';
            csvBtn.style.cursor = 'pointer';
            csvBtn.onclick = () => {
                const headers = [
                    '模型名称', 'Code', '免费额度', '倒计时显示', '到期时间',
                    ...(userSettings.showModelType ? ['模型类型'] : []),
                    ...(userSettings.showContextLength ? ['上下文长度'] : []),
                    ...(userSettings.showPrice ? ['价格'] : []),
                    ...(userSettings.showProtocol ? ['模型协议'] : []),
                    ...(userSettings.showLimit ? ['限流'] : []),
                    ...(userSettings.showDescription ? ['描述'] : []),
                    ...(userSettings.showVendor ? ['供应商'] : []),
                    ...(userSettings.showUpdateTime ? ['更新时间'] : [])
                ];

                const rows = extractedData.map(d => [
                    d.name,
                    d.code,
                    d.freeQuota,
                    `剩余 ${d.daysLeft} 天`,
                    d.expiry,
                    ...(userSettings.showModelType ? [d.modelType || '—'] : []),
                    ...(userSettings.showContextLength ? [d.contextLength || '—'] : []),
                    ...(userSettings.showPrice ? [d.price || '—'] : []),
                    ...(userSettings.showProtocol ? [d.protocol || '—'] : []),
                    ...(userSettings.showLimit ? [d.limit || '—'] : []),
                    ...(userSettings.showDescription ? [d.description || '—'] : []),
                    ...(userSettings.showVendor ? [d.vendor || '—'] : []),
                    ...(userSettings.showUpdateTime ? [d.updateTime || '—'] : [])
                ].map(s => `"${String(s).replace(/"/g, '""')}"`).join(','));

                const csv = [headers.join(','), ...rows].join('\n');
                navigator.clipboard.writeText(csv).then(() => {
                    csvBtn.textContent = '✅ 已复制!';
                    setTimeout(() => csvBtn.textContent = '📋 复制为 CSV', 2000);
                });
            };
            content.appendChild(csvBtn);
        }

        const close = document.createElement('span');
        close.textContent = '×';
        close.style.position = 'absolute'; close.style.top = '10px'; close.style.right = '16px';
        close.style.fontSize = '24px'; close.style.cursor = 'pointer';
        close.onclick = () => document.body.removeChild(modal);
        content.appendChild(close);

        modal.appendChild(content);
        document.body.appendChild(modal);
    }

    // 工具函数:创建表格单元格
    function appendCell(tr, text, style = {}) {
        const td = document.createElement('td');
        td.style.padding = '10px';
        td.style.border = '1px solid #ddd';
        Object.assign(td.style, style);
        td.textContent = text;
        tr.appendChild(td);
    }

    function appendCodeCell(tr, code) {
        const td = document.createElement('td');
        td.style.padding = '10px';
        td.style.border = '1px solid #ddd';
        td.style.cursor = 'pointer';
        td.style.color = '#007cba';
        td.style.fontWeight = 'bold';
        td.title = '点击复制 Code';
        td.textContent = code;
        td.onclick = () => {
            GM_setClipboard(code);
            td.textContent = '✅ 已复制!';
            setTimeout(() => td.textContent = code, 1500);
        };
        tr.appendChild(td);
    }

    function appendCountdownCell(tr, daysLeft) {
        const td = document.createElement('td');
        td.style.padding = '10px';
        td.style.border = '1px solid #ddd';
        td.style.fontWeight = 'bold';
        td.style.color = daysLeft < 30 ? '#d9534f' :
                        daysLeft < 90 ? '#f0ad4e' : '#5cb85c';
        td.textContent = `剩余 ${daysLeft} 天`;
        tr.appendChild(td);
    }

    // 创建设置按钮和弹窗
    function createSettingsPanel() {
        const settingsBtnId = 'bailian-settings-btn';
        if (document.getElementById(settingsBtnId)) return;

        // 设置按钮
        const settingsBtn = document.createElement('button');
        settingsBtn.id = settingsBtnId;
        Object.assign(settingsBtn.style, {
            position: 'fixed', top: '140px', right: '20px', zIndex: '2147483646',
            backgroundColor: '#4CAF50', color: 'white', border: 'none',
            padding: '10px 14px', borderRadius: '8px', cursor: 'pointer',
            fontSize: '16px', fontWeight: 'bold', boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
            opacity: 0.95, fontFamily: 'Arial, sans-serif'
        });
        settingsBtn.textContent = '⚙️ 设置';
        settingsBtn.title = '点击打开设置面板';

        settingsBtn.addEventListener('click', () => {
            showSettingsModal();
        });

        document.body.appendChild(settingsBtn);
        log('✅ 设置按钮已创建');
    }

    // 显示设置弹窗
    function showSettingsModal() {
        const modalId = 'bailian-settings-modal';
        if (document.getElementById(modalId)) document.body.removeChild(document.getElementById(modalId));

        const modal = document.createElement('div');
        modal.id = modalId;
        Object.assign(modal.style, {
            position: 'fixed', top: 0, left: 0, width: '100%', height: '100%',
            backgroundColor: 'rgba(0,0,0,0.6)', display: 'flex', alignItems: 'center',
            justifyContent: 'center', zIndex: '2147483647', fontFamily: 'Arial, sans-serif'
        });

        const content = document.createElement('div');
        Object.assign(content.style, {
            backgroundColor: 'white', width: '90%', maxWidth: '500px', padding: '20px',
            borderRadius: '10px', position: 'relative'
        });

        const title = document.createElement('h3');
        title.textContent = '🔧 设置面板';
        content.appendChild(title);

        const form = document.createElement('div');
        form.style.marginTop = '15px';

        const fields = [
            { key: 'showModelType', label: '显示模型类型' },
            { key: 'showContextLength', label: '显示上下文长度' },
            { key: 'showPrice', label: '显示价格' },
            { key: 'showProtocol', label: '显示模型协议' },
            { key: 'showLimit', label: '显示限流' },
            { key: 'showDescription', label: '显示描述' },
            { key: 'showVendor', label: '显示供应商(仅主页面)' },
            { key: 'showUpdateTime', label: '显示更新时间(仅主页面)' }
        ];

        fields.forEach(field => {
            const div = document.createElement('div');
            div.style.marginBottom = '12px';

            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.id = `setting-${field.key}`;
            checkbox.checked = userSettings[field.key];
            checkbox.onchange = () => {
                userSettings[field.key] = checkbox.checked;
                localStorage.setItem('bailian_user_settings', JSON.stringify(userSettings));
            };

            const label = document.createElement('label');
            label.htmlFor = `setting-${field.key}`;
            label.textContent = field.label;
            label.style.marginLeft = '8px';

            div.appendChild(checkbox);
            div.appendChild(label);
            form.appendChild(div);
        });

        content.appendChild(form);

        const saveBtn = document.createElement('button');
        saveBtn.textContent = '✅ 保存并关闭';
        saveBtn.style.marginTop = '20px';
        saveBtn.style.padding = '10px 16px';
        saveBtn.style.backgroundColor = '#007cba';
        saveBtn.style.color = 'white';
        saveBtn.style.border = 'none';
        saveBtn.style.borderRadius = '4px';
        saveBtn.style.cursor = 'pointer';
        saveBtn.onclick = () => {
            document.body.removeChild(modal);
            alert('✅ 设置已保存,下次提取时生效。');
        };
        content.appendChild(saveBtn);

        const close = document.createElement('span');
        close.textContent = '×';
        close.style.position = 'absolute'; close.style.top = '10px'; close.style.right = '16px';
        close.style.fontSize = '24px'; close.style.cursor = 'pointer';
        close.onclick = () => document.body.removeChild(modal);
        content.appendChild(close);

        modal.appendChild(content);
        document.body.appendChild(modal);
    }

    // 初始化函数优化
    function init() {
        console.log(LOG_PREFIX, '脚本已注入,版本:', GM_info.script.version);

        // 等待DOM准备就绪后创建按钮
        if (document.readyState === 'loading') {
            // 使用更快速的DOM加载检测
            const checkInterval = setInterval(() => {
                if (document.readyState === 'interactive' || document.readyState === 'complete') {
                    clearInterval(checkInterval);
                    setTimeout(createFloatingButton, 50); // 极短等待
                }
            }, 30);

            // 超时处理
            setTimeout(() => {
                clearInterval(checkInterval);
                setTimeout(createFloatingButton, 50);
            }, 1000);
        } else {
            setTimeout(createFloatingButton, 50);
        }
    }

    init();
})();