关于wordpress网站运行状态界面的添加

Lee 0 次阅读 发布于 2 天前 最后更新于 1 天前 4971 字 预计阅读时间: 23 分钟


#前言

wordpress由于其中心化和插件化为管理带来了不少的便捷,但是面对一些奇奇怪怪的要求时候我们有可能找不到对应的插件,比如在一个全新的页面展示网站的运行状态(如下)

#教程

可以新建一个php页面来写,但为了效率建议在根目录放置css和js,优化体验。

#代码

:root {
    --bg: #06090f;
    --fg: #e6fffa;
    --muted: #8aa5b7;
    --card: rgba(10, 12, 16, 0.62);
    --card-border: rgba(0, 255, 170, 0.08);
    --shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
    --accent: #00ffaa;
    --accent-soft: rgba(0, 255, 170, 0.14);
    --grid-rgb: 0, 255, 170;
}
html,
body {
    height: 100%;
    margin: 0;
}
body {
    background: var(--bg);
    color: var(--fg);
    font: 16px/1.6 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
    transition: background 0.6s ease;
}
.status-wrapper {
    position: relative;
    min-height: 100%;
    overflow: hidden;
}
.status-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    z-index: 0;
}
.status-container {
    position: relative;
    z-index: 1;
    max-width: 1100px;
    margin: 64px auto;
    padding: 0 20px 60px;
}
.status-header {
    margin-bottom: 18px;
}
.status-title {
    font-size: 30px;
    margin: 0 0 6px 0;
    font-weight: 600;
}
.theme-switcher {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 20px;
    flex-wrap: wrap;
}
.theme-switcher .label {
    font-size: 13px;
    color: var(--muted);
}
.theme-button {
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.06);
    color: var(--fg);
    padding: 6px 14px;
    border-radius: 999px;
    font-size: 13px;
    cursor: pointer;
    transition: all 0.2s ease;
    backdrop-filter: blur(6px);
}
.theme-button:hover {
    transform: translateY(-1px);
    border-color: rgba(var(--grid-rgb), 0.4);
    box-shadow: 0 0 18px rgba(var(--grid-rgb), 0.15);
}
.theme-button.active {
    background: var(--accent-soft);
    color: var(--accent);
    border-color: rgba(var(--grid-rgb), 0.55);
    box-shadow: 0 0 22px rgba(var(--grid-rgb), 0.2);
}
.custom-panel {
    display: none;
    gap: 14px;
    padding: 14px 18px;
    margin-bottom: 22px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 18px;
    backdrop-filter: blur(12px);
    flex-wrap: wrap;
    align-items: flex-end;
}
.custom-panel.active {
    display: flex;
}
.custom-panel .field {
    display: flex;
    flex-direction: column;
    min-width: 150px;
}
.custom-panel label {
    font-size: 12px;
    color: var(--muted);
    margin-bottom: 6px;
}
.custom-panel input[type="color"] {
    width: 52px;
    height: 32px;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    background: none;
    padding: 0;
}
.custom-panel select,
.custom-panel input[type="range"],
.custom-panel input[type="number"] {
    background: rgba(0, 0, 0, 0.25);
    color: var(--fg);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
    padding: 6px 10px;
    font-size: 13px;
}
.custom-panel input[type="range"] {
    width: 160px;
    height: 30px;
    padding: 0;
    margin-top: 4px;
}
.custom-panel .hint {
    font-size: 12px;
    color: var(--muted);
    flex: 1 1 100%;
    margin-top: 4px;
}
.status-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 18px;
}
.status-card {
    background: var(--card);
    backdrop-filter: blur(14px);
    border-radius: 16px;
    padding: 18px 20px;
    box-shadow: var(--shadow);
    border: 1px solid var(--card-border);
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.status-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 22px 55px rgba(0, 0, 0, 0.35);
}
.status-label {
    font-size: 13px;
    color: var(--muted);
}
.status-value {
    font-size: 22px;
    font-weight: 500;
    margin-top: 8px;
}
.status-bar {
    height: 6px;
    background: rgba(255, 255, 255, 0.08);
    border-radius: 999px;
    overflow: hidden;
    margin-top: 12px;
}
.status-bar-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--accent), rgba(var(--grid-rgb), 0.45));
}
.status-badge {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.02em;
}
.status-footer {
    margin-top: 28px;
    color: var(--muted);
    font-size: 13px;
    text-align: right;
}
body::before,
.status-wrapper::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(1200px 580px at 50% 18%, rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.82));
    z-index: 0;
    transition: background 0.6s ease;
}
body[data-theme="cyber"] {
    --bg: #06090f;
    --fg: #e6fffa;
    --muted: #8aa5b7;
    --card: rgba(10, 12, 16, 0.62);
    --card-border: rgba(0, 255, 170, 0.08);
    --shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
    --accent: #00ffaa;
    --accent-soft: rgba(0, 255, 170, 0.14);
    --grid-rgb: 0, 255, 170;
}
body[data-theme="aurora"] {
    --bg: #050720;
    --fg: #f5f7ff;
    --muted: #a9b8ff;
    --card: rgba(18, 20, 45, 0.68);
    --card-border: rgba(138, 165, 255, 0.12);
    --shadow: 0 22px 50px rgba(12, 18, 48, 0.5);
    --accent: #7af5ff;
    --accent-soft: rgba(122, 245, 255, 0.16);
    --grid-rgb: 122, 245, 255;
}
body[data-theme="dusk"] {
    --bg: #050308;
    --fg: #fff5ff;
    --muted: #d8b0ff;
    --card: rgba(26, 12, 30, 0.7);
    --card-border: rgba(255, 86, 201, 0.14);
    --shadow: 0 22px 55px rgba(12, 6, 18, 0.55);
    --accent: #ff56c9;
    --accent-soft: rgba(255, 86, 201, 0.16);
    --grid-rgb: 255, 86, 201;
}
body[data-theme="ocean"] {
    --bg: #02090f;
    --fg: #e6fbff;
    --muted: #8ac4d9;
    --card: rgba(10, 30, 45, 0.64);
    --card-border: rgba(90, 200, 255, 0.16);
    --shadow: 0 22px 55px rgba(0, 20, 30, 0.52);
    --accent: #00b3ff;
    --accent-soft: rgba(0, 179, 255, 0.18);
    --grid-rgb: 0, 179, 255;
}
body[data-theme="sunset"] {
    --bg: #120207;
    --fg: #fff4f1;
    --muted: #ffbdb3;
    --card: rgba(45, 11, 24, 0.68);
    --card-border: rgba(255, 120, 120, 0.16);
    --shadow: 0 24px 58px rgba(28, 6, 12, 0.55);
    --accent: #ff6b6b;
    --accent-soft: rgba(255, 107, 107, 0.18);
    --grid-rgb: 255, 107, 107;
}
body[data-theme="matrix"] {
    --bg: #020409;
    --fg: #dfffee;
    --muted: #7cd9ba;
    --card: rgba(8, 18, 18, 0.7);
    --card-border: rgba(0, 255, 170, 0.18);
    --shadow: 0 24px 60px rgba(0, 20, 20, 0.55);
    --accent: #00f0a8;
    --accent-soft: rgba(0, 240, 168, 0.18);
    --grid-rgb: 0, 240, 168;
}
body[data-theme="starfire"] {
    --bg: #03051a;
    --fg: #f4faff;
    --muted: #9ecbff;
    --card: rgba(12, 18, 45, 0.7);
    --card-border: rgba(100, 170, 255, 0.14);
    --shadow: 0 24px 60px rgba(3, 8, 30, 0.55);
    --accent: #6fe0ff;
    --accent-soft: rgba(111, 224, 255, 0.2);
    --grid-rgb: 111, 224, 255;
}
body[data-theme="custom"] {
    --bg: #050810;
    --fg: #e7faff;
    --muted: #a0bfd1;
    --card: rgba(12, 18, 28, 0.7);
    --card-border: rgba(255, 255, 255, 0.12);
    --shadow: 0 24px 58px rgba(5, 10, 16, 0.55);
}
.status-wrapper::before {
    pointer-events: none;
}
.status-footer a {
    color: inherit;
}
@media (max-width: 720px) {
    .status-container {
        margin: 32px auto;
        padding: 0 16px 40px;
    }
    .status-title {
        font-size: 26px;
    }
}
(function () {
    const CONFIG_ELEMENT_ID = 'status-theme-config';
    const CANVAS_ID = 'statusCanvas';
    const STORAGE_THEME_KEY = 'statusTheme';
    const STORAGE_CUSTOM_KEY = 'statusCustom';
    const themePayload = document.getElementById(CONFIG_ELEMENT_ID);
    if (!themePayload) {
        console.warn('[status-theme] missing config payload');
        return;
    }
    let config;
    try {
        config = JSON.parse(themePayload.textContent || '{}');
    } catch (err) {
        console.error('[status-theme] failed to parse config', err);
        return;
    }
    const canvas = document.getElementById(CANVAS_ID);
    if (!canvas) {
        console.warn('[status-theme] missing canvas element');
        return;
    }
    const ctx = canvas.getContext('2d');
    const themeButtons = Array.from(document.querySelectorAll('.theme-button'));
    const customPanel = document.getElementById('customPanel');
    const customInputs = {
        accent: document.getElementById('customAccent'),
        mode: document.getElementById('customMode'),
        density: document.getElementById('customDensity'),
        speed: document.getElementById('customSpeed')
    };
    const presets = config.presets || {};
    const customDefaults = config.customDefaults || {
        accent: '#00ffaa',
        mode: 'pixelFlow',
        density: 72,
        speed: 100
    };
    const state = {
        themeId: config.activeTheme || 'cyber',
        preset: null,在·
        mode: 'pixelFlow',
        dotSize: 2,
        density: 72,
        speed: 1,
        amplitude: 0.6,
        accentRGB: '0,255,170',
        custom: loadCustomSettings(),
        animating: false,
        frame: 0,
        elements: [],
        extra: {}
    };
    let animationId = null;
    let dpr = window.devicePixelRatio || 1;
    // -------------------- Utility --------------------
    function loadCustomSettings() {
        try {
            const stored = localStorage.getItem(STORAGE_CUSTOM_KEY);
            if (stored) {
                return Object.assign({}, customDefaults, JSON.parse(stored));
            }
        } catch (err) {
            console.warn('[status-theme] unable to read custom settings', err);
        }
        return Object.assign({}, customDefaults);
    }
    function persistCustomSettings() {
        try {
            localStorage.setItem(STORAGE_CUSTOM_KEY, JSON.stringify(state.custom));
        } catch (err) {
            console.warn('[status-theme] unable to persist custom settings', err);
        }
    }
    function persistTheme(themeId) {
        try {
            localStorage.setItem(STORAGE_THEME_KEY, themeId);
        } catch (err) {
            console.warn('[status-theme] unable to persist theme', err);
        }
    }
    function hexToRgb(hex) {
        const cleaned = (hex || '').replace('#', '').trim();
        if (cleaned.length === 3) {
            return cleaned.split('').map(ch => parseInt(ch + ch, 16));
        }
        if (cleaned.length === 6) {
            return [cleaned.slice(0, 2), cleaned.slice(2, 4), cleaned.slice(4, 6)].map(part => parseInt(part, 16));
        }
        return [0, 255, 170];
    }
    function computeAccentRGB() {
        const style = getComputedStyle(document.body);
        let rgb = style.getPropertyValue('--grid-rgb').trim();
        if (rgb) {
            return rgb;
        }
        const accent = style.getPropertyValue('--accent').trim();
        if (accent && accent.startsWith('#')) {
            const [r, g, b] = hexToRgb(accent);
            return `${r},${g},${b}`;
        }
        return '0,255,170';
    }
    function setCustomVariables(settings) {
        const [r, g, b] = hexToRgb(settings.accent || '#00ffaa');
        document.body.style.setProperty('--accent', settings.accent || '#00ffaa');
        document.body.style.setProperty('--accent-soft', `rgba(${r},${g},${b},0.16)`);
        document.body.style.setProperty('--grid-rgb', `${r},${g},${b}`);
    }
    function clearCustomVariables() {
        ['--accent', '--accent-soft', '--grid-rgb'].forEach(prop => document.body.style.removeProperty(prop));
    }
    function refreshCanvasSize() {
        dpr = window.devicePixelRatio || 1;
        canvas.width = Math.round(window.innerWidth * dpr);
        canvas.height = Math.round(window.innerHeight * dpr);
        rebuildElements();
    }
    function rebuildElements() {
        const width = canvas.width;
        const height = canvas.height;
        const densityScale = (state.density || 70) / 70;
        state.elements = [];
        state.extra = {};
        if (state.mode === 'pixelFlow') {
            const spacing = Math.max(8, 28 - densityScale * 10);
            const cols = Math.ceil(width / spacing);
            const rows = Math.ceil(height / spacing);
            for (let y = 0; y <= rows; y++) {
                for (let x = 0; x <= cols; x++) {
                    state.elements.push({
                        x: x * spacing,
                        y: y * spacing,
                        seedX: Math.random() * 1000,
                        seedY: Math.random() * 1000
                    });
                }
            }
        } else if (state.mode === 'matrix') {
            const columns = Math.max(28, Math.round(50 * densityScale));
            const columnWidth = width / columns;
            state.extra.columns = [];
            for (let i = 0; i < columns; i++) {
                state.extra.columns.push({
                    x: i * columnWidth + columnWidth * 0.5,
                    y: -Math.random() * height,
                    length: (Math.random() * 0.5 + 0.4) * height,
                    speed: (Math.random() * 0.5 + 0.6) * state.speed * 60
                });
            }
        } else if (state.mode === 'stars') {
            const count = Math.round(180 * densityScale);
            for (let i = 0; i < count; i++) {
                state.elements.push({
                    x: Math.random() * width,
                    y: Math.random() * height,
                    size: (Math.random() * 1.6 + 0.5) * dpr,
                    speed: (Math.random() * 0.4 + 0.15) * state.speed * 40,
                    phase: Math.random() * Math.PI * 2
                });
            }
        } else if (state.mode === 'waves') {
            const waveCount = Math.max(2, Math.round(3 * densityScale));
            state.elements = new Array(waveCount).fill(null).map((_, index) => ({
                amplitude: (40 + 20 * index) * state.amplitude,
                wavelength: 0.0015 + index * 0.0006,
                speed: (0.3 + index * 0.12) * state.speed,
                phase: Math.random() * Math.PI * 2,
                alpha: 0.18 + index * 0.12
            }));
        } else { // dots fallback
            const spacing = Math.max(10, 32 - densityScale * 12);
            const cols = Math.ceil(width / spacing);
            const rows = Math.ceil(height / spacing);
            for (let y = 0; y <= rows; y++) {
                for (let x = 0; x <= cols; x++) {
                    state.elements.push({
                        x: x * spacing,
                        y: y * spacing,
                        offset: Math.random() * 100
                    });
                }
            }
        }
    }
    // -------------------- Noise --------------------
    function FlowNoise(seed) {
        const perm = new Uint8Array(512);
        const gradX = new Float32Array(512);
        const gradY = new Float32Array(512);
        let s = seed || Date.now();
        function random() {
            s = (s * 1664525 + 1013904223) % 4294967296;
            return s / 4294967296;
        }
        const gradients = [
            [1, 0], [-1, 0], [0, 1], [0, -1],
            [Math.SQRT1_2, Math.SQRT1_2], [-Math.SQRT1_2, Math.SQRT1_2],
            [Math.SQRT1_2, -Math.SQRT1_2], [-Math.SQRT1_2, -Math.SQRT1_2]
        ];
        for (let i = 0; i < 256; i++) {
            const g = gradients[i % gradients.length];
            gradX[i] = g[0];
            gradY[i] = g[1];
            perm[i] = i;
        }
        for (let i = 255; i > 0; i--) {
            const j = Math.floor(random() * (i + 1));
            const tmp = perm[i];
            perm[i] = perm[j];
            perm[j] = tmp;
        }
        for (let i = 0; i < 256; i++) {
            perm[i + 256] = perm[i];
            gradX[i + 256] = gradX[i];
            gradY[i + 256] = gradY[i];
        }
        function fade(t) {
            return t * t * t * (t * (t * 6 - 15) + 10);
        }
        function lerp(a, b, t) {
            return a + t * (b - a);
        }
        function grad(hash, x, y) {
            return gradX[hash] * x + gradY[hash] * y;
        }
        this.noise = function (x, y) {
            const ix = Math.floor(x) & 255;
            const iy = Math.floor(y) & 255;
            const fx = x - Math.floor(x);
            const fy = y - Math.floor(y);
            const u = fade(fx);
            const v = fade(fy);
            const aa = perm[ix + perm[iy]];
            const ab = perm[ix + perm[iy + 1]];
            const ba = perm[ix + 1 + perm[iy]];
            const bb = perm[ix + 1 + perm[iy + 1]];
            const x1 = lerp(grad(aa, fx, fy), grad(ba, fx - 1, fy), u);
            const x2 = lerp(grad(ab, fx, fy - 1), grad(bb, fx - 1, fy - 1), u);
            return lerp(x1, x2, v);
        };
    }
    const flowNoise = new FlowNoise(config.noiseSeed || 20251107);
    // -------------------- Animation --------------------
    function renderFrame(timestamp) {
        const width = canvas.width;
        const height = canvas.height;
        const accent = state.accentRGB;
        ctx.clearRect(0, 0, width, height);
        const time = timestamp * 0.001 * state.speed;
        if (state.mode === 'pixelFlow') {
            const dotSize = (state.dotSize || 2) * dpr;
            const amplitude = Math.max(12, state.amplitude * 35);
            for (let i = 0; i < state.elements.length; i++) {
                const dot = state.elements[i];
                const flow = flowNoise.noise(dot.seedX + time * 0.35, dot.seedY + time * 0.35);
                const flow2 = flowNoise.noise(dot.seedX - time * 0.28, dot.seedY + time * 0.22);
                const px = dot.x * dpr + flow * amplitude;
                const py = dot.y * dpr + flow2 * amplitude;
                const brightness = 0.35 + 0.45 * Math.sin(time * 2 + dot.seedX);
                ctx.fillStyle = `rgba(${accent}, ${0.08 + 0.45 * brightness})`;
                ctx.fillRect(px, py, dotSize, dotSize);
            }
        } else if (state.mode === 'matrix' && state.extra.columns) {
            ctx.fillStyle = 'rgba(0,0,0,0.12)';
            ctx.fillRect(0, 0, width, height);
            const colWidth = width / (state.extra.columns.length || 1);
            for (let i = 0; i < state.extra.columns.length; i++) {
                const column = state.extra.columns[i];
                column.y += column.speed;
                if (column.y - column.length > height) {
                    column.y = -Math.random() * height * 0.4;
                    column.length = (Math.random() * 0.4 + 0.3) * height;
                    column.speed = (Math.random() * 0.5 + 0.6) * state.speed * 60;
                }
                const x = column.x * dpr;
                const grad = ctx.createLinearGradient(x, (column.y - column.length) * dpr, x, column.y * dpr);
                grad.addColorStop(0, `rgba(${accent},0)`);
                grad.addColorStop(0.2, `rgba(${accent},0.15)`);
                grad.addColorStop(0.6, `rgba(${accent},0.55)`);
                grad.addColorStop(1, `rgba(${accent},0)`);
                ctx.fillStyle = grad;
                ctx.fillRect(
                    x,
                    (column.y - column.length) * dpr,
                    Math.max(2, state.dotSize * dpr * 1.4),
                    column.length * dpr
                );
            }
        } else if (state.mode === 'stars') {
            for (let i = 0; i < state.elements.length; i++) {
                const star = state.elements[i];
                star.phase += 0.02 * state.speed;
                const flick = 0.45 + 0.5 * Math.sin(time * 2 + star.phase);
                ctx.beginPath();
                ctx.fillStyle = `rgba(${accent}, ${0.15 + flick * 0.6})`;
                ctx.arc(star.x * dpr, star.y * dpr, star.size, 0, Math.PI * 2);
                ctx.fill();
                star.y += star.speed * 0.016;
                if (star.y > height + 10) {
                    star.y = -Math.random() * 60;
                    star.x = Math.random() * width;
                }
            }
        } else if (state.mode === 'waves') {
            ctx.lineWidth = Math.max(1.2 * dpr, 0.8);
            for (let i = 0; i < state.elements.length; i++) {
                const wave = state.elements[i];
                wave.phase += 0.6 * wave.speed * 0.016;
                ctx.beginPath();
                ctx.strokeStyle = `rgba(${accent},${wave.alpha})`;
                const segment = 6 * dpr;
                for (let x = 0; x <= width; x += segment) {
                    const y = height * 0.5 + Math.sin(x * wave.wavelength + time * wave.speed + wave.phase) * wave.amplitude;
                    const px = x;
                    const py = y * dpr;
                    if (x === 0) ctx.moveTo(px, py);
                    else ctx.lineTo(px, py);
                }
                ctx.stroke();
            }
        } else { // dots fallback
            const dotSize = (state.dotSize || 2) * dpr;
            const amplitude = Math.max(10, state.amplitude * 30);
            for (let i = 0; i < state.elements.length; i++) {
                const dot = state.elements[i];
                const nx = dot.x * dpr + Math.sin(time + dot.offset) * amplitude;
                const ny = dot.y * dpr + Math.cos(time * 1.2 + dot.offset) * amplitude;
                const flick = 0.4 + 0.4 * Math.sin(time * 2 + dot.offset);
                ctx.fillStyle = `rgba(${accent}, ${0.08 + 0.4 * flick})`;
                ctx.fillRect(nx, ny, dotSize, dotSize);
            }
        }
        animationId = requestAnimationFrame(renderFrame);
    }
    function stopAnimation() {
        if (animationId) {
            cancelAnimationFrame(animationId);
            animationId = null;
        }
    }
    function startAnimation() {
        stopAnimation();
        animationId = requestAnimationFrame(renderFrame);
    }
    function applyTheme(themeId, persist = false) {
        if (!presets[themeId]) {
            themeId = 'cyber';
        }
        const preset = Object.assign({}, presets[themeId]);
        if (preset.customizable) {
            customPanel?.classList.add('active');
            setCustomVariables(state.custom);
            syncCustomInputs();
            preset.mode = state.custom.mode || preset.mode;
            preset.density = Number(state.custom.density || preset.density || 70);
            const speedFactor = (state.custom.speed || 100) / 100;
            preset.speed = (preset.speed || 1) * speedFactor;
        } else {
            customPanel?.classList.remove('active');
            clearCustomVariables();
        }
        state.themeId = themeId;
        state.preset = preset;
        state.mode = preset.mode || 'pixelFlow';
        state.density = preset.density || 70;
        state.speed = preset.speed || 1;
        state.dotSize = preset.dotSize || 2;
        state.amplitude = preset.amplitude || 0.6;
        state.accentRGB = computeAccentRGB();
        themeButtons.forEach(btn => btn.classList.toggle('active', btn.dataset.theme === themeId));
        if (persist) {
            persistTheme(themeId);
        }
        refreshCanvasSize();
        startAnimation();
    }
    function syncCustomInputs() {
        if (!customPanel) return;
        if (customInputs.accent) customInputs.accent.value = state.custom.accent || customDefaults.accent;
        if (customInputs.mode) customInputs.mode.value = state.custom.mode || customDefaults.mode;
        if (customInputs.density) customInputs.density.value = state.custom.density || customDefaults.density;
        if (customInputs.speed) customInputs.speed.value = state.custom.speed || customDefaults.speed;
    }
    function handleCustomChange() {
        if (!customPanel) return;
        if (customInputs.accent) state.custom.accent = customInputs.accent.value;
        if (customInputs.mode) state.custom.mode = customInputs.mode.value;
        if (customInputs.density) state.custom.density = Number(customInputs.density.value);
        if (customInputs.speed) state.custom.speed = Number(customInputs.speed.value);
        persistCustomSettings();
        if (state.themeId === 'custom') {
            setCustomVariables(state.custom);
            applyTheme('custom', true);
        }
    }
    // -------------------- Init --------------------
    themeButtons.forEach(button => {
        button.addEventListener('click', () => {
            applyTheme(button.dataset.theme, true);
        });
    });
    if (customPanel) {
        Object.values(customInputs).forEach(input => {
            if (!input) return;
            input.addEventListener('change', handleCustomChange);
            if (input.type === 'range' || input.type === 'color') {
                input.addEventListener('input', handleCustomChange);
            }
        });
    }
    window.addEventListener('resize', () => {
        clearTimeout(state.resizeTimer);
        state.resizeTimer = setTimeout(refreshCanvasSize, 120);
    });
    let initialTheme = state.themeId;
    try {
        const stored = localStorage.getItem(STORAGE_THEME_KEY);
        if (stored && presets[stored]) {
            initialTheme = stored;
        }
    } catch (err) {
        console.warn('[status-theme] unable to read stored theme', err);
    }
    applyTheme(initialTheme, false);
})();
<?php
// 独立网站状态页(不依赖插件)。将本文件放在 WordPress 根目录,并通过 https://你的域名/site-status.php 访问。

// 可选:加载 WordPress 环境以便读取数据库信息与版本(若不需要可注释掉)。
$wp_bootstrap_loaded = false;
$wp_root = __DIR__ . DIRECTORY_SEPARATOR;
if (file_exists($wp_root . 'wp-load.php')) {
    require_once $wp_root . 'wp-load.php';
    $wp_bootstrap_loaded = true;
}

// ------------------------- 指标采集 -------------------------
function ss_safe_shell($cmd) {
    if (!function_exists('shell_exec') || ini_get('safe_mode')) return null;
    return @shell_exec($cmd);
}

function ss_path_allowed($path) {
    $restrictions = ini_get('open_basedir');
    if (empty($restrictions)) return true;
    $path_real = @realpath($path);
    $path_check = $path_real ? $path_real : $path;
    $paths = preg_split('/[:;]/', $restrictions);
    foreach ($paths as $allowed) {
        $allowed = rtrim($allowed);
        if ($allowed === '') continue;
        $allowed_real = @realpath($allowed);
        if ($allowed_real && strncasecmp($path_check, $allowed_real, strlen($allowed_real)) === 0) {
            return true;
        }
    }
    return false;
}

function ss_format_bytes($bytes, $precision = 2) {
    $units = array('B','KB','MB','GB','TB');
    for ($i = 0; $bytes > 1024 && $i < count($units) - 1; $i++) {
        $bytes /= 1024;
    }
    return round($bytes, $precision).' '.$units[$i];
}

function ss_cpu_usage() {
    $os = strtolower(PHP_OS);
    if (strpos($os, 'linux') !== false && ss_path_allowed('/proc/stat') && is_readable('/proc/stat')) {
        $stat1 = @file('/proc/stat');
        usleep(200000);
        $stat2 = @file('/proc/stat');
        if ($stat1 && $stat2) {
            $info1 = preg_split('/\s+/', trim($stat1[0]));
            $info2 = preg_split('/\s+/', trim($stat2[0]));
            $idle1 = (int)$info1[4];
            $total1 = array_sum(array_map('intval', array_slice($info1, 1)));
            $idle2 = (int)$info2[4];
            $total2 = array_sum(array_map('intval', array_slice($info2, 1)));
            $diff_total = max(1, $total2 - $total1);
            $diff_idle = max(0, $idle2 - $idle1);
            $usage = (1 - $diff_idle / $diff_total) * 100;
            return round($usage, 1);
        }
    }
    $out = ss_safe_shell('wmic cpu get LoadPercentage');
    if ($out) {
        foreach (explode("\n", $out) as $line) {
            $line = trim($line);
            if (is_numeric($line)) return (float)$line;
        }
    }
    return null;
}

function ss_loadavg() {
    if (function_exists('sys_getloadavg')) {
        $l = sys_getloadavg();
        return implode(', ', $l);
    }
    $out = ss_safe_shell('wmic cpu get LoadPercentage');
    if ($out) {
        foreach (explode("\n", $out) as $line) {
            $line = trim($line);
            if (is_numeric($line)) {
                $c = 4;
                $v = (float)$line / 100 * $c;
                return sprintf('%.2f, %.2f, %.2f', $v, $v, $v);
            }
        }
    }
    return 'N/A';
}

function ss_memory() {
    $os = strtolower(PHP_OS);
    if (strpos($os, 'linux') !== false && ss_path_allowed('/proc/meminfo') && is_readable('/proc/meminfo')) {
        $meminfo = file('/proc/meminfo');
        $vals = array();
        foreach ($meminfo as $row) {
            if (preg_match('/^(\w+):\s+(\d+)/', $row, $m)) {
                $vals[$m[1]] = (int)$m[2];
            }
        }
        if (isset($vals['MemTotal']) && isset($vals['MemAvailable'])) {
            $total = $vals['MemTotal'] * 1024;
            $avail = $vals['MemAvailable'] * 1024;
            $used = $total - $avail;
            $percent = $total > 0 ? round($used / $total * 100) : 0;
            return array(
                'total' => $total,
                'used' => $used,
                'percent' => $percent,
                'total_f' => ss_format_bytes($total),
                'used_f' => ss_format_bytes($used)
            );
        }
    }
    $out = ss_safe_shell('wmic OS get FreePhysicalMemory,TotalVisibleMemorySize /Value');
    if ($out && preg_match('/TotalVisibleMemorySize=(\d+)/', $out, $t) && preg_match('/FreePhysicalMemory=(\d+)/', $out, $f)) {
        $total = $t[1] * 1024;
        $free = $f[1] * 1024;
        $used = $total - $free;
        $p = $total > 0 ? round($used / $total * 100) : 0;
        return array(
            'total' => $total,
            'used' => $used,
            'percent' => $p,
            'total_f' => ss_format_bytes($total),
            'used_f' => ss_format_bytes($used)
        );
    }
    $limit = ini_get('memory_limit');
    $limit_bytes = 0;
    if ($limit && $limit !== '-1') {
        $unit = strtolower(substr($limit, -1));
        $value = (int)$limit;
        switch ($unit) {
            case 'g': $value *= 1024;
            case 'm': $value *= 1024;
            case 'k': $value *= 1024; break;
        }
        $limit_bytes = $value;
    }
    $used_bytes = function_exists('memory_get_usage') ? memory_get_usage(true) : 0;
    $percent = $limit_bytes > 0 ? round($used_bytes / $limit_bytes * 100) : 0;
    return array(
        'total' => $limit_bytes,
        'used' => $used_bytes,
        'percent' => max(0, min(100, $percent)),
        'total_f' => $limit_bytes > 0 ? ss_format_bytes($limit_bytes) : 'N/A',
        'used_f' => $used_bytes > 0 ? ss_format_bytes($used_bytes) : 'N/A'
    );
}

function ss_disk() {
    $root = __DIR__;
    $total = @disk_total_space($root);
    $free = @disk_free_space($root);
    if ($total !== false && $free !== false) {
        $used = $total - $free;
        $p = $total > 0 ? round($used / $total * 100) : 0;
        return array(
            'total' => $total,
            'used' => $used,
            'percent' => $p,
            'total_f' => ss_format_bytes($total),
            'used_f' => ss_format_bytes($used)
        );
    }
    return array('total' => 0, 'used' => 0, 'percent' => 0, 'total_f' => 'N/A', 'used_f' => 'N/A');
}

function ss_db() {
    $res = array('connected' => false, 'size_f' => 'N/A', 'version' => 'N/A');
    if (!defined('DB_NAME')) return $res;
    global $wpdb;
    try {
        if (isset($wpdb) && $wpdb->check_connection()) {
            $res['connected'] = true;
            $size = $wpdb->get_var($wpdb->prepare("SELECT SUM(data_length+index_length) FROM information_schema.TABLES WHERE table_schema=%s", DB_NAME));
            if ($size) $res['size_f'] = ss_format_bytes((int)$size);
            $res['version'] = $wpdb->db_version();
        }
    } catch (Exception $e) {}
    return $res;
}

$metrics = array(
    'cpu' => ss_cpu_usage(),
    'load' => ss_loadavg(),
    'mem' => ss_memory(),
    'disk' => ss_disk(),
    'db' => $wp_bootstrap_loaded ? ss_db() : array('connected' => false, 'size_f' => 'N/A', 'version' => 'N/A'),
    'wp_version' => $wp_bootstrap_loaded ? get_bloginfo('version') : 'N/A',
    'php_version' => PHP_VERSION,
);

$theme_presets = array(
    'cyber' => array('label' => '赛博矩阵', 'mode' => 'pixelFlow', 'speed' => 1.1, 'dotSize' => 2, 'amplitude' => 0.65, 'density' => 72),
    'aurora' => array('label' => '极光幻境', 'mode' => 'pixelFlow', 'speed' => 0.85, 'dotSize' => 2.4, 'amplitude' => 0.9, 'density' => 64),
    'matrix' => array('label' => '霓虹矩阵', 'mode' => 'matrix', 'speed' => 1.35, 'dotSize' => 2.3, 'amplitude' => 0.75, 'density' => 88),
    'starfire' => array('label' => '星火宇宙', 'mode' => 'stars', 'speed' => 0.7, 'dotSize' => 2.6, 'amplitude' => 0.45, 'density' => 82),
    'ocean' => array('label' => '深海蓝潮', 'mode' => 'waves', 'speed' => 0.95, 'amplitude' => 1.1, 'density' => 68),
    'custom' => array('label' => '自定义', 'mode' => 'pixelFlow', 'speed' => 1, 'dotSize' => 2, 'amplitude' => 0.6, 'density' => 72, 'customizable' => true),
);

$requested_theme = isset($_GET['style']) ? preg_replace('/[^a-z0-9_-]/i', '', $_GET['style']) : '';
$active_theme = $requested_theme && isset($theme_presets[$requested_theme]) ? $requested_theme : 'cyber';

$theme_config = array(
    'presets' => $theme_presets,
    'activeTheme' => $active_theme,
    'customDefaults' => array(
        'accent' => '#00ffaa',
        'mode' => 'pixelFlow',
        'density' => 72,
        'speed' => 100
    ),
    'noiseSeed' => 20251107
);

$theme_config_json = json_encode($theme_config, JSON_UNESCAPED_UNICODE);
?>
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>网站运行状态</title>
    <link rel="stylesheet" href="site-status-assets/css/status-themes.css?v=1.0">
</head>
<body data-theme="<?php echo htmlspecialchars($active_theme, ENT_QUOTES, 'UTF-8'); ?>">
<div class="status-wrapper">
    <canvas class="status-canvas" id="statusCanvas"></canvas>
    <div class="status-container">
        <div class="status-header">
            <h1 class="status-title">Site Status Dashboard</h1>
            <div class="status-label">Last updated: <?php echo date('Y-m-d H:i:s'); ?></div>
        </div>
        <?php if (count($theme_presets) > 1): ?>
        <div class="theme-switcher" role="group" aria-label="Theme selector">
            <span class="label">主题风格:</span>
            <?php foreach ($theme_presets as $theme_id => $theme_info): ?>
                <button type="button" class="theme-button<?php echo $theme_id === $active_theme ? ' active' : ''; ?>" data-theme="<?php echo htmlspecialchars($theme_id, ENT_QUOTES, 'UTF-8'); ?>">
                    <?php echo htmlspecialchars($theme_info['label'], ENT_QUOTES, 'UTF-8'); ?>
                </button>
            <?php endforeach; ?>
        </div>
        <?php endif; ?>
        <div id="customPanel" class="custom-panel">
            <div class="field">
                <label for="customAccent">主题主色</label>
                <input type="color" id="customAccent" value="#00ffaa">
            </div>
            <div class="field">
                <label for="customMode">背景动效</label>
                <select id="customMode">
                    <option value="pixelFlow">像素流场</option>
                    <option value="matrix">霓虹下落</option>
                    <option value="stars">星轨闪烁</option>
                    <option value="waves">流光曲线</option>
                    <option value="dots">柔和粒子</option>
                </select>
            </div>
            <div class="field">
                <label for="customDensity">动效密度</label>
                <input type="range" id="customDensity" min="40" max="120" value="72">
            </div>
            <div class="field" style="min-width:200px;">
                <label for="customSpeed">速度倍数</label>
                <input type="range" id="customSpeed" min="40" max="160" value="100">
            </div>
            <div class="hint">提示:切换到“自定义”主题后,上述参数将立即生效并保存。</div>
        </div>
        <div class="status-cards">
            <div class="status-card">
                <div class="status-label">Server Load</div>
                <div class="status-value"><?php echo htmlspecialchars($metrics['load']); ?></div>
                <div class="status-label" style="margin-top:8px">CPU</div>
                <div class="status-bar"><div class="status-bar-fill" style="width: <?php echo (float)($metrics['cpu'] ?? 0); ?>%"></div></div>
                <div class="status-label"><?php echo (float)($metrics['cpu'] ?? 0); ?>%</div>
            </div>
            <div class="status-card">
                <div class="status-label">Memory Usage</div>
                <div class="status-value"><?php echo $metrics['mem']['used_f']; ?> / <?php echo $metrics['mem']['total_f']; ?></div>
                <div class="status-bar"><div class="status-bar-fill" style="width: <?php echo (int)$metrics['mem']['percent']; ?>%"></div></div>
            </div>
            <div class="status-card">
                <div class="status-label">Disk Usage</div>
                <div class="status-value"><?php echo $metrics['disk']['used_f']; ?> / <?php echo $metrics['disk']['total_f']; ?></div>
                <div class="status-bar"><div class="status-bar-fill" style="width: <?php echo (int)$metrics['disk']['percent']; ?>%"></div></div>
            </div>
            <div class="status-card">
                <div class="status-label">Database</div>
                <div class="status-value"><?php echo $metrics['db']['connected'] ? '<span class="status-badge">Connected</span>' : '<span class="status-badge" style="background:#402728;color:#ffd0d0">Disconnected</span>'; ?></div>
                <div class="status-label" style="margin-top:6px">Size: <?php echo htmlspecialchars($metrics['db']['size_f']); ?> | MySQL: <?php echo htmlspecialchars($metrics['db']['version']); ?></div>
            </div>
            <div class="status-card">
                <div class="status-label">Environment</div>
                <div class="status-value">PHP <?php echo htmlspecialchars($metrics['php_version']); ?><?php if ($metrics['wp_version'] !== 'N/A') { echo ' · WordPress ' . $metrics['wp_version']; } ?></div>
            </div>
        </div>
        <div class="status-footer">
            Powered by standalone status page · <?php echo $_SERVER['HTTP_HOST']; ?> &nbsp;|
            <a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener">苏ICP备2025217910号-1</a>
        </div>
    </div>
</div>
<script type="application/json" id="status-theme-config"><?php echo $theme_config_json; ?></script>
<script src="site-status-assets/js/status-themes.js?v=1.0" defer></script>
</body>
</html>

  • reward_image1
此作者没有提供个人介绍。
最后更新于 2025-11-08