// ==UserScript== // @name WordPress 后台管理助手 // @namespace https://weiruan.com/ // @version 1.3.0 // @description WordPress后台快捷管理工具:发布文章、查看访客数据、网站设置等 // @author Weiruan // @match *://*/* // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @connect * // @run-at document-end // @downloadURL https://update.greasyfork.icu/scripts/562429/WordPress%20%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%E5%8A%A9%E6%89%8B.user.js // @updateURL https://update.greasyfork.icu/scripts/562429/WordPress%20%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%E5%8A%A9%E6%89%8B.meta.js // ==/UserScript== (function() { 'use strict'; // ==================== 配置管理 ==================== const Config = { get: (key, defaultValue = null) => { try { return GM_getValue(key, defaultValue); } catch(e) { const val = localStorage.getItem('wp_admin_' + key); return val ? JSON.parse(val) : defaultValue; } }, set: (key, value) => { try { GM_setValue(key, value); } catch(e) { localStorage.setItem('wp_admin_' + key, JSON.stringify(value)); } } }; // ==================== 样式注入 ==================== const styles = ` /* ==================== CSS Reset for Panel ==================== */ .wp-admin-panel *, .wp-admin-panel *::before, .wp-admin-panel *::after, .wp-admin-trigger *, .wp-config-overlay * { box-sizing: border-box; margin: 0; padding: 0; } /* ==================== CSS Variables ==================== */ :root { --wp-primary: #0073aa; --wp-primary-dark: #005a87; --wp-success: #46b450; --wp-warning: #ffb900; --wp-danger: #dc3232; --wp-bg: #ffffff; --wp-bg-alt: #f6f7f7; --wp-text: #1e1e1e; --wp-text-light: #646970; --wp-border: #dcdcde; --wp-radius: 8px; } .wp-admin-panel.dark-theme { --wp-primary: #3582c4; --wp-bg: #1e1e1e; --wp-bg-alt: #2c2c2c; --wp-text: #ffffff; --wp-text-light: #a0a0a0; --wp-border: #3c3c3c; } /* ==================== Trigger Button ==================== */ .wp-admin-trigger { position: fixed; right: 20px; bottom: 20px; width: 56px; height: 56px; background: linear-gradient(135deg, var(--wp-primary) 0%, var(--wp-primary-dark) 100%); border-radius: 50%; cursor: pointer; z-index: 999998; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 15px rgba(0,115,170,0.4); transition: all 0.3s ease; border: none; } .wp-admin-trigger:hover { transform: scale(1.1); box-shadow: 0 6px 20px rgba(0,115,170,0.5); } .wp-admin-trigger svg { width: 28px; height: 28px; fill: white; } /* ==================== Main Panel ==================== */ .wp-admin-panel { position: fixed; right: 20px; bottom: 90px; width: 400px; max-height: 70vh; background: var(--wp-bg); border-radius: var(--wp-radius); box-shadow: 0 4px 20px rgba(0,0,0,0.15); z-index: 999999; display: none; flex-direction: column; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; font-size: 14px; color: var(--wp-text); } .wp-admin-panel.active { display: flex; animation: wpSlideIn 0.3s ease; } @keyframes wpSlideIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } /* ==================== Panel Header ==================== */ .wp-panel-header { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; background: linear-gradient(135deg, var(--wp-primary) 0%, var(--wp-primary-dark) 100%); color: white; border-radius: var(--wp-radius) var(--wp-radius) 0 0; } .wp-panel-header h3 { font-size: 16px; font-weight: 600; display: flex; align-items: center; gap: 8px; } .wp-panel-header h3 svg { width: 22px; height: 22px; fill: currentColor; } .wp-panel-header-actions { display: flex; gap: 6px; } .wp-panel-header-btn { width: 30px; height: 30px; background: rgba(255,255,255,0.2); border: none; border-radius: 6px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: white; transition: background 0.2s; } .wp-panel-header-btn:hover { background: rgba(255,255,255,0.3); } .wp-panel-header-btn svg { width: 18px; height: 18px; fill: currentColor; } /* ==================== Connection Status ==================== */ .wp-connection-status { padding: 8px 16px; font-size: 12px; border-bottom: 1px solid var(--wp-border); } .wp-connection-status.connected { background: #d4edda; color: #155724; } .wp-connection-status.disconnected { background: #f8d7da; color: #721c24; } /* ==================== Tabs ==================== */ .wp-panel-tabs { display: flex; background: var(--wp-bg-alt); border-bottom: 1px solid var(--wp-border); } .wp-panel-tab { flex: 1; padding: 10px 6px; text-align: center; cursor: pointer; border: none; background: transparent; color: var(--wp-text-light); font-size: 12px; display: flex; flex-direction: column; align-items: center; gap: 4px; transition: all 0.2s; } .wp-panel-tab:hover { background: var(--wp-bg); color: var(--wp-primary); } .wp-panel-tab.active { background: var(--wp-bg); color: var(--wp-primary); box-shadow: inset 0 -2px 0 var(--wp-primary); } .wp-panel-tab svg { width: 20px; height: 20px; fill: currentColor; } /* ==================== Content Area ==================== */ .wp-panel-content { flex: 1; overflow-y: auto; padding: 16px; max-height: calc(70vh - 140px); } .wp-panel-section { display: none; } .wp-panel-section.active { display: block; } /* ==================== Form Elements ==================== */ .wp-form-group { margin-bottom: 14px; } .wp-form-label { display: block; margin-bottom: 6px; font-weight: 500; color: var(--wp-text); font-size: 13px; } .wp-form-input, .wp-form-select, .wp-form-textarea { width: 100%; padding: 10px 12px; border: 1px solid var(--wp-border); border-radius: 6px; font-size: 14px; background: var(--wp-bg); color: var(--wp-text); transition: border-color 0.2s, box-shadow 0.2s; } .wp-form-input:focus, .wp-form-select:focus, .wp-form-textarea:focus { outline: none; border-color: var(--wp-primary); box-shadow: 0 0 0 3px rgba(0,115,170,0.1); } .wp-form-textarea { min-height: 100px; resize: vertical; } /* ==================== Editor ==================== */ .wp-editor-toolbar { display: flex; flex-wrap: wrap; gap: 4px; padding: 8px; background: var(--wp-bg-alt); border: 1px solid var(--wp-border); border-bottom: none; border-radius: 6px 6px 0 0; } .wp-editor-btn { width: 30px; height: 30px; border: none; background: var(--wp-bg); border-radius: 4px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--wp-text); transition: all 0.2s; } .wp-editor-btn:hover { background: var(--wp-primary); color: white; } .wp-editor-btn svg { width: 16px; height: 16px; fill: currentColor; } .wp-editor-content { min-height: 150px; max-height: 200px; overflow-y: auto; padding: 12px; border: 1px solid var(--wp-border); border-radius: 0 0 6px 6px; background: var(--wp-bg); outline: none; } .wp-editor-content:focus { border-color: var(--wp-primary); } .wp-editor-content:empty:before { content: attr(data-placeholder); color: var(--wp-text-light); } /* ==================== Buttons ==================== */ .wp-btn { padding: 10px 16px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; display: inline-flex; align-items: center; justify-content: center; gap: 6px; transition: all 0.2s; } .wp-btn-primary { background: var(--wp-primary); color: white; } .wp-btn-primary:hover { background: var(--wp-primary-dark); } .wp-btn-primary:disabled { background: #ccc; cursor: not-allowed; } .wp-btn-secondary { background: var(--wp-bg-alt); color: var(--wp-text); border: 1px solid var(--wp-border); } .wp-btn-secondary:hover { background: var(--wp-border); } .wp-btn-danger { background: var(--wp-danger); color: white; } .wp-btn-block { width: 100%; } .wp-btn-group { display: flex; gap: 8px; } .wp-btn svg { width: 16px; height: 16px; fill: currentColor; } /* ==================== Stats Grid ==================== */ .wp-stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin-bottom: 16px; } .wp-stat-card { padding: 14px; background: var(--wp-bg-alt); border-radius: 8px; text-align: center; } .wp-stat-value { font-size: 24px; font-weight: 700; color: var(--wp-primary); margin-bottom: 4px; } .wp-stat-label { font-size: 11px; color: var(--wp-text-light); } /* ==================== List Items ==================== */ .wp-list-item { padding: 10px; border-bottom: 1px solid var(--wp-border); display: flex; align-items: center; justify-content: space-between; gap: 10px; } .wp-list-item:last-child { border-bottom: none; } .wp-list-item-title { font-weight: 500; font-size: 13px; margin-bottom: 2px; } .wp-list-item-meta { font-size: 11px; color: var(--wp-text-light); } /* ==================== Post Items ==================== */ .wp-post-item { padding: 12px; border: 1px solid var(--wp-border); border-radius: 6px; margin-bottom: 8px; background: var(--wp-bg); } .wp-post-item-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 6px; gap: 8px; } .wp-post-item-title { font-weight: 600; font-size: 13px; color: var(--wp-text); flex: 1; line-height: 1.4; } .wp-post-item-status { font-size: 10px; padding: 2px 6px; border-radius: 10px; background: var(--wp-bg-alt); white-space: nowrap; } .wp-post-item-status.publish { background: #d4edda; color: #155724; } .wp-post-item-status.draft { background: #fff3cd; color: #856404; } .wp-post-item-actions { display: flex; gap: 6px; margin-top: 8px; } .wp-post-item-btn { padding: 4px 10px; font-size: 12px; border: none; border-radius: 4px; cursor: pointer; background: var(--wp-bg-alt); color: var(--wp-text); transition: all 0.2s; } .wp-post-item-btn:hover { background: var(--wp-border); } .wp-post-item-btn.wp-btn-danger { background: var(--wp-danger); color: white; } /* ==================== Settings ==================== */ .wp-settings-item { display: flex; align-items: center; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--wp-border); } .wp-settings-item:last-child { border-bottom: none; } .wp-toggle { position: relative; width: 44px; height: 24px; background: var(--wp-border); border-radius: 12px; cursor: pointer; transition: background 0.3s; } .wp-toggle.active { background: var(--wp-success); } .wp-toggle::after { content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; background: white; border-radius: 50%; transition: transform 0.3s; } .wp-toggle.active::after { transform: translateX(20px); } /* ==================== Quick Actions ==================== */ .wp-quick-actions { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; } .wp-quick-action { padding: 14px 8px; background: var(--wp-bg-alt); border: 1px solid var(--wp-border); border-radius: 8px; text-align: center; cursor: pointer; transition: all 0.2s; text-decoration: none; color: var(--wp-text); } .wp-quick-action:hover { border-color: var(--wp-primary); transform: translateY(-2px); } .wp-quick-action svg { width: 22px; height: 22px; margin-bottom: 6px; fill: var(--wp-primary); } .wp-quick-action-label { font-size: 11px; font-weight: 500; } /* ==================== Charts ==================== */ .wp-chart-container { padding: 14px; background: var(--wp-bg-alt); border-radius: 8px; margin-bottom: 14px; } .wp-chart-title { font-weight: 600; font-size: 13px; margin-bottom: 10px; } .wp-chart-bar { display: flex; align-items: center; margin-bottom: 6px; gap: 8px; } .wp-chart-bar-label { width: 70px; font-size: 11px; color: var(--wp-text-light); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .wp-chart-bar-track { flex: 1; height: 18px; background: var(--wp-bg); border-radius: 4px; overflow: hidden; } .wp-chart-bar-fill { height: 100%; background: linear-gradient(90deg, var(--wp-primary), #00a0d2); border-radius: 4px; transition: width 0.5s ease; } .wp-chart-bar-value { width: 45px; text-align: right; font-size: 11px; font-weight: 600; } /* ==================== Info Box ==================== */ .wp-info-box { padding: 10px 12px; background: #e7f3ff; border: 1px solid #b3d7ff; border-radius: 6px; margin-bottom: 14px; font-size: 12px; color: #0056b3; line-height: 1.5; } /* ==================== Empty State ==================== */ .wp-empty { text-align: center; padding: 30px 16px; color: var(--wp-text-light); } .wp-empty svg { width: 40px; height: 40px; margin-bottom: 10px; opacity: 0.5; fill: currentColor; } .wp-empty p { margin: 0; font-size: 13px; } /* ==================== Toast ==================== */ .wp-toast { position: fixed; top: 20px; right: 20px; padding: 12px 18px; background: white; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.2); z-index: 1000000; display: flex; align-items: center; gap: 10px; animation: wpToastIn 0.3s ease; color: #333; font-size: 14px; } .wp-toast.success { border-left: 4px solid var(--wp-success); } .wp-toast.error { border-left: 4px solid var(--wp-danger); } .wp-toast.warning { border-left: 4px solid var(--wp-warning); } @keyframes wpToastIn { from { opacity: 0; transform: translateX(100px); } to { opacity: 1; transform: translateX(0); } } /* ==================== Loading ==================== */ .wp-loading { display: inline-block; width: 18px; height: 18px; border: 2px solid var(--wp-border); border-top-color: var(--wp-primary); border-radius: 50%; animation: wpSpin 0.8s linear infinite; } @keyframes wpSpin { to { transform: rotate(360deg); } } /* ==================== Config Modal ==================== */ .wp-config-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 1000000; display: flex; align-items: center; justify-content: center; } .wp-config-modal { width: 90%; max-width: 450px; background: white; border-radius: var(--wp-radius); padding: 20px; max-height: 80vh; overflow-y: auto; color: #333; } .wp-config-modal h3 { margin: 0 0 16px; font-size: 18px; } /* ==================== Section Titles ==================== */ .wp-section-title { font-size: 13px; font-weight: 600; margin: 16px 0 10px; color: var(--wp-text); } .wp-section-title:first-child { margin-top: 0; } `; if (typeof GM_addStyle !== 'undefined') { GM_addStyle(styles); } else { const styleEl = document.createElement('style'); styleEl.textContent = styles; document.head.appendChild(styleEl); } // ==================== 图标库 ==================== const Icons = { wordpress: ``, edit: ``, chart: ``, settings: ``, more: ``, close: ``, config: ``, moon: ``, bold: ``, italic: ``, underline: ``, list: ``, link: ``, image: ``, heading: ``, quote: ``, posts: ``, comments: ``, users: ``, theme: ``, plugin: ``, refresh: ``, view: ``, delete: ``, check: ``, }; // ==================== API 封装 (使用 GM_xmlhttpRequest) ==================== class WordPressAPI { constructor(siteUrl, username = '', appPassword = '') { this.siteUrl = siteUrl.replace(/\/$/, ''); this.apiBase = `${this.siteUrl}/wp-json/wp/v2`; this.username = username; this.appPassword = appPassword; this.useRestRoute = false; // 某些站点需要使用 ?rest_route= 格式 } // 使用 GM_xmlhttpRequest 进行跨域请求 request(endpoint, options = {}) { return new Promise((resolve, reject) => { const url = endpoint.startsWith('http') ? endpoint : `${this.apiBase}${endpoint}`; const method = options.method || 'GET'; const headers = { 'Content-Type': 'application/json', ...options.headers }; // 添加认证头 if (this.username && this.appPassword) { // 应用密码可能包含空格,需要保留 const credentials = `${this.username}:${this.appPassword}`; const auth = btoa(unescape(encodeURIComponent(credentials))); headers['Authorization'] = `Basic ${auth}`; console.log('[WP Admin] 认证用户:', this.username, '| 密码长度:', this.appPassword.length); } else { console.warn('[WP Admin] 警告: 未设置认证信息! username:', !!this.username, 'appPassword:', !!this.appPassword); } console.log('[WP Admin] 请求:', method, url); console.log('[WP Admin] 有Authorization头:', 'Authorization' in headers); GM_xmlhttpRequest({ method: method, url: url, headers: headers, data: options.body || null, onload: (response) => { console.log('[WP Admin] 响应:', response.status, response.finalUrl || url); if (response.status === 401 || response.status === 403) { console.error('[WP Admin] 认证失败! 请检查用户名和应用密码'); } try { if (response.status >= 200 && response.status < 300) { // 检查是否是JSON const contentType = response.responseHeaders?.toLowerCase() || ''; if (response.responseText.trim().startsWith('<')) { console.error('[WP Admin] 返回了HTML而非JSON:', response.responseText.substring(0, 200)); reject(new Error('服务器返回HTML而非JSON,URL可能不正确: ' + url)); return; } const data = JSON.parse(response.responseText); resolve(data); } else { let errorMsg = `HTTP ${response.status}`; let errorCode = ''; try { const errData = JSON.parse(response.responseText); errorMsg = errData.message || errorMsg; errorCode = errData.code || ''; console.error('[WP Admin] API错误:', errData); } catch(e) { // 如果不是JSON,可能是HTML错误页 if (response.responseText.includes('<')) { errorMsg += ' (返回了HTML页面)'; } } // 特殊处理认证错误 if (errorCode === 'rest_cannot_create' || errorCode === 'rest_cannot_edit') { errorMsg += '\n\n请检查:\n1. 用户名是否正确\n2. 应用密码是否有效\n3. 用户是否有发布权限'; } reject(new Error(errorMsg)); } } catch (e) { reject(new Error('解析响应失败: ' + e.message + ' | URL: ' + url)); } }, onerror: (error) => { reject(new Error('网络请求失败,请检查站点地址是否正确')); }, ontimeout: () => { reject(new Error('请求超时')); } }); }); } // 测试连接 - 尝试多个端点 async testConnection() { const endpoints = [ `${this.siteUrl}/wp-json/wp/v2/posts?per_page=1`, `${this.siteUrl}/wp-json`, `${this.siteUrl}/index.php?rest_route=/wp/v2/posts&per_page=1`, `${this.siteUrl}/?rest_route=/wp/v2/posts&per_page=1`, `${this.siteUrl}/index.php?rest_route=/`, `${this.siteUrl}/?rest_route=/` ]; for (const endpoint of endpoints) { try { console.log('[WP Admin] 测试端点:', endpoint); const result = await this.request(endpoint); // 检测是否是WordPress REST API响应 if (result && (Array.isArray(result) || result.name || result.namespaces)) { // 检测使用的模式 if (endpoint.includes('index.php?rest_route')) { this.useRestRoute = true; this.restRoutePrefix = '/index.php'; console.log('[WP Admin] 使用 index.php?rest_route 模式'); } else if (endpoint.includes('?rest_route')) { this.useRestRoute = true; this.restRoutePrefix = ''; console.log('[WP Admin] 使用 ?rest_route 模式'); } else { this.useRestRoute = false; console.log('[WP Admin] 使用标准 /wp-json 模式'); } // 尝试获取当前用户信息 try { const user = await this.getCurrentUser(); if (user && user.id) { this.currentUserId = user.id; console.log('[WP Admin] 当前用户ID:', user.id, '用户名:', user.name); } } catch (userError) { console.log('[WP Admin] 无法获取用户信息(可能需要认证):', userError.message); } return { success: true }; } } catch (error) { console.log('[WP Admin] 端点失败:', endpoint, error.message); continue; } } return { success: false, error: 'REST API不可用。请检查:\n1. 站点地址是否正确\n2. WordPress版本是否≥4.7\n3. REST API是否被插件禁用\n4. 是否有插件干扰首页输出' }; } // 构建API URL buildUrl(endpoint) { // 移除开头的 /wp/v2 如果存在 const cleanEndpoint = endpoint.replace(/^\/wp\/v2/, ''); let finalUrl; if (this.useRestRoute) { const prefix = this.restRoutePrefix || ''; // 使用 ?rest_route= 格式 // 需要处理查询参数:/posts?per_page=10 -> rest_route=/wp/v2/posts&per_page=10 if (cleanEndpoint.includes('?')) { const [path, query] = cleanEndpoint.split('?'); finalUrl = `${this.siteUrl}${prefix}?rest_route=/wp/v2${path}&${query}`; } else { finalUrl = `${this.siteUrl}${prefix}?rest_route=/wp/v2${cleanEndpoint}`; } } else { finalUrl = `${this.apiBase}${cleanEndpoint}`; } console.log('[WP Admin] buildUrl:', endpoint, '->', finalUrl, '| useRestRoute:', this.useRestRoute); return finalUrl; } // 文章相关 async getPosts(params = {}) { const query = new URLSearchParams({ per_page: 10, ...params }).toString(); return this.request(this.buildUrl(`/wp/v2/posts?${query}`)); } async createPost(data) { return this.request(this.buildUrl('/wp/v2/posts'), { method: 'POST', body: JSON.stringify(data) }); } async updatePost(id, data) { return this.request(this.buildUrl(`/wp/v2/posts/${id}`), { method: 'POST', body: JSON.stringify(data) }); } async deletePost(id) { return this.request(this.buildUrl(`/wp/v2/posts/${id}?force=true`), { method: 'DELETE' }); } // 分类和标签 async getCategories() { return this.request(this.buildUrl('/wp/v2/categories?per_page=100')); } async getTags() { return this.request(this.buildUrl('/wp/v2/tags?per_page=100')); } // 评论 async getComments(params = {}) { const query = new URLSearchParams({ per_page: 10, ...params }).toString(); return this.request(this.buildUrl(`/wp/v2/comments?${query}`)); } // 用户信息 async getCurrentUser() { return this.request(this.buildUrl('/wp/v2/users/me')); } // 获取当前用户ID(带缓存) async getCurrentUserId() { if (this.currentUserId) return this.currentUserId; try { const user = await this.getCurrentUser(); this.currentUserId = user.id; return user.id; } catch (e) { console.error('[WP Admin] 获取用户ID失败:', e); return null; } } // 站点信息 async getSiteInfo() { return this.request(this.buildUrl('/')); } // 获取统计数据 - 基于文章数据 async getStats() { try { // 获取所有已发布文章 const allPosts = await this.request(this.buildUrl('/wp/v2/posts?per_page=100&status=publish')); const totalPosts = allPosts.length; // 计算时间范围内的文章 const now = new Date(); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000); const monthAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000); let todayPosts = 0, weekPosts = 0, monthPosts = 0; const postStats = []; allPosts.forEach(post => { const postDate = new Date(post.date); if (postDate >= today) todayPosts++; if (postDate >= weekAgo) weekPosts++; if (postDate >= monthAgo) monthPosts++; postStats.push({ id: post.id, title: post.title.rendered.replace(/<[^>]*>/g, ''), date: post.date, link: post.link, views: post.post_views_count || post.views || 0 // Post Views Counter 插件 }); }); // 获取评论数 const comments = await this.request(this.buildUrl('/wp/v2/comments?per_page=100')); const totalComments = comments.length; return { posts: { total: totalPosts, today: todayPosts, week: weekPosts, month: monthPosts }, comments: totalComments, recentPosts: postStats.slice(0, 5), popularPosts: [...postStats].sort((a, b) => (b.views || 0) - (a.views || 0)).slice(0, 5) }; } catch (error) { console.error('获取统计失败:', error); throw error; } } // 尝试获取访客统计数据 async getVisitorStats() { const result = { available: false, plugin: null, today: 0, week: 0, month: 0, total: 0, visitors: { today: 0, total: 0 } }; // 尝试 WP Statistics 插件 try { const wpStatsUrl = this.useRestRoute ? `${this.siteUrl}${this.restRoutePrefix || ''}?rest_route=/wp-statistics/v2/hit` : `${this.siteUrl}/wp-json/wp-statistics/v2/hit`; const wpStats = await this.request(wpStatsUrl); if (wpStats) { result.available = true; result.plugin = 'WP Statistics'; console.log('[WP Admin] WP Statistics 数据:', wpStats); return result; } } catch (e) { console.log('[WP Admin] WP Statistics 不可用'); } // 尝试 Jetpack Stats try { const jetpackUrl = this.useRestRoute ? `${this.siteUrl}${this.restRoutePrefix || ''}?rest_route=/jetpack/v4/module/stats` : `${this.siteUrl}/wp-json/jetpack/v4/module/stats`; const jetpack = await this.request(jetpackUrl); if (jetpack) { result.available = true; result.plugin = 'Jetpack Stats'; console.log('[WP Admin] Jetpack Stats 数据:', jetpack); return result; } } catch (e) { console.log('[WP Admin] Jetpack Stats 不可用'); } // 尝试 Koko Analytics try { const kokoUrl = this.useRestRoute ? `${this.siteUrl}${this.restRoutePrefix || ''}?rest_route=/koko-analytics/v1/stats` : `${this.siteUrl}/wp-json/koko-analytics/v1/stats`; const koko = await this.request(kokoUrl); if (koko) { result.available = true; result.plugin = 'Koko Analytics'; result.today = koko.pageviews?.today || 0; result.total = koko.pageviews?.total || 0; result.visitors.today = koko.visitors?.today || 0; result.visitors.total = koko.visitors?.total || 0; console.log('[WP Admin] Koko Analytics 数据:', koko); return result; } } catch (e) { console.log('[WP Admin] Koko Analytics 不可用'); } // 尝试自定义统计端点 (通用格式) try { const customUrl = this.useRestRoute ? `${this.siteUrl}${this.restRoutePrefix || ''}?rest_route=/wp-site-stats/v1/stats` : `${this.siteUrl}/wp-json/wp-site-stats/v1/stats`; const custom = await this.request(customUrl); if (custom) { result.available = true; result.plugin = 'Site Stats'; result.today = custom.views?.today || custom.today || 0; result.week = custom.views?.week || custom.week || 0; result.month = custom.views?.month || custom.month || 0; result.total = custom.views?.total || custom.total || 0; return result; } } catch (e) { console.log('[WP Admin] 自定义统计端点不可用'); } // 尝试从文章中提取 Post Views Counter 数据 try { const posts = await this.request(this.buildUrl('/wp/v2/posts?per_page=100&status=publish')); let totalViews = 0; let hasViewsData = false; posts.forEach(post => { const views = post.post_views_count || post.views || post.meta?.post_views_count || 0; if (views > 0) { hasViewsData = true; totalViews += parseInt(views) || 0; } }); if (hasViewsData) { result.available = true; result.plugin = 'Post Views Counter'; result.total = totalViews; // 估算其他数据(无法精确获取) result.today = Math.round(totalViews / 100) || 0; result.week = Math.round(totalViews / 15) || 0; result.month = Math.round(totalViews / 4) || 0; return result; } } catch (e) { console.log('[WP Admin] Post Views Counter 不可用'); } return result; } } // ==================== 主应用 ==================== class WPAdminPanel { constructor() { this.api = null; this.siteUrl = Config.get('siteUrl', ''); this.username = Config.get('username', ''); this.appPassword = Config.get('appPassword', ''); this.darkMode = Config.get('darkMode', false); this.connected = false; this.categories = []; this.init(); } init() { this.createUI(); this.bindEvents(); this.registerMenuCommand(); console.log('[WP Admin] 初始化 - siteUrl:', this.siteUrl, 'username:', this.username, 'hasPassword:', !!this.appPassword); if (this.siteUrl && this.username && this.appPassword) { this.api = new WordPressAPI(this.siteUrl, this.username, this.appPassword); // 恢复保存的useRestRoute状态 this.api.useRestRoute = Config.get('useRestRoute', false); this.api.restRoutePrefix = Config.get('restRoutePrefix', ''); console.log('[WP Admin] API已创建 - useRestRoute:', this.api.useRestRoute); this.testAndLoad(); } } async testAndLoad() { // 重新测试连接,确保useRestRoute状态正确 const result = await this.api.testConnection(); this.connected = result.success; // 保存检测到的模式 if (this.connected) { Config.set('useRestRoute', this.api.useRestRoute); } this.updateConnectionStatus(); if (this.connected) { this.loadInitialData(); } } updateConnectionStatus() { const statusEl = this.panel.querySelector('#wp-connection-status'); if (statusEl) { if (this.connected) { statusEl.className = 'wp-connection-status connected'; statusEl.textContent = '✓ 已连接到 ' + this.siteUrl; } else { statusEl.className = 'wp-connection-status disconnected'; statusEl.textContent = '✗ 未连接 - 请配置站点'; } } } registerMenuCommand() { if (typeof GM_registerMenuCommand !== 'undefined') { GM_registerMenuCommand('配置WordPress站点', () => this.showConfigModal()); GM_registerMenuCommand('发布新文章', () => this.openPanel('publish')); GM_registerMenuCommand('查看统计', () => this.openPanel('stats')); } } createUI() { const trigger = document.createElement('button'); trigger.className = 'wp-admin-trigger'; trigger.innerHTML = Icons.wordpress; trigger.title = 'WordPress 管理助手'; document.body.appendChild(trigger); this.trigger = trigger; const panel = document.createElement('div'); panel.className = `wp-admin-panel ${this.darkMode ? 'dark-theme' : ''}`; panel.innerHTML = this.getPanelHTML(); document.body.appendChild(panel); this.panel = panel; } getPanelHTML() { return `
请先配置站点
请先配置站点
请先配置站点
暂无文章
暂无文章
暂无评论