MENU

Typecho 后台美化插件 - Admin Beautify

• 2026 年 03 月 08 日 • 阅读: 593 • 技术,开发

这可能是史上颜值最高的 Typecho 后台美化插件

目录

🎨 Admin Beautify (原Login Beautify)

Typecho 后台美化插件
Admin Beautify

✨ 介绍

功能特性

  • 🎨 Material Design 3 风格 — 全面采用 MD3 设计规范
  • 🌈 7 种主题色 — 紫、蓝、青、绿、橙、粉、红,一键切换
  • 🌗 暗色模式 — 支持亮色 / 暗色 两种模式
  • 过渡动画 — 丝滑的界面过渡动画,可开关
  • 📌 导航栏位置 — 支持侧边栏(默认)和顶部导航栏两种布局
  • 🔐 登录页美化 — 独立的登录页面美化,支持:

    • 12+ 配色预设方案(紫、蓝、粉、绿、橙、红、青、靛蓝、日落渐变、海洋渐变等)
    • 自定义主色 / 辅色
    • 背景图片 + 可调虚化效果
    • 站点名称显示 / 隐藏
    • 独立的暗色模式控制
    • 自定义 CSS / JS 注入
  • AJAX 导航 — 无刷新页面切换,后台操作更流畅
  • 📱 PWA 响应式 APP — 安装到您的电脑/手机,流畅管理博客 (v2.1.0 加入)
  • 🧩 兼容脚本设计 — 自动修复其他插件的排版和功能 (v2.1.0 加入)

📦 安装方法

  1. 前往 GitHub Releases 下载最新版本
  2. 解压后将 AdminBeautify 文件夹上传至
/usr/plugins/
  1. 后台进入
    控制台 → 插件 → 启用 AdminBeautify

📸 截图预览

首页插件管理个人设置
图片.png图片.png图片.png
顶部导航栏(原版)文章编辑页面文章管理
插件仓库移动端导航栏
AB插件仓库

❓ 常见问题

Q: 支持哪些版本的 Typecho?

本插件基于 Typecho 1.3.0 设计开发,建议使用该版本或更高版本。

Q: Typecho 1.2.1兼容吗?

已写 Typecho 1.2.1 兼容脚本(目前不够完善),请在 插件设置-兼容脚本 中开启,如果没有该脚本请点击同步Github按钮。

Q: 启用后后台样式异常怎么办?

请先清除浏览器缓存,确保加载最新的 CSS/JS 文件。如果仍有问题,请检查是否与其他后台美化插件冲突。

Q: 移动端显示效果如何?

插件内置了响应式设计,移动端会自动切换为顶部折叠菜单模式。

Q: 如何自定义登录页背景?

在插件设置中填入背景图片的 URL,并可调整虚化方式和虚化大小。

🧩 插件样式适配 (主题色/亮暗适配)

本节面向 其他 Typecho 插件的开发者,说明如何让你的插件原生兼容 Admin Beautify 的样式体系和暗色模式,而无需依赖兼容脚本。

一、暗色模式兼容

Admin Beautify 通过在 <html> 标签上切换 data-theme 属性来控制暗色模式:

data-theme="light"   // 亮色
data-theme="dark"    // 暗色

推荐做法: 在你的插件 CSS 中,使用 [data-theme="dark"] 选择器覆盖暗色样式,而不是依赖 @media (prefers-color-scheme: dark)(因为 Admin Beautify 支持手动切换,不一定跟随系统)。

/* ✅ 推荐:跟随 Admin Beautify 的暗色切换 */
[data-theme="dark"] .my-plugin-card {
    background: #1d1b20;
    color: #e6e1e5;
    border-color: rgba(255, 255, 255, .12);
}

/* ⚠️ 不推荐:仅跟随系统,无法响应手动切换 */
@media (prefers-color-scheme: dark) {
    .my-plugin-card { background: #1d1b20; }
}

二、CSS 自定义变量(Design Tokens)

Admin Beautify 在 :root 上注入了一套完整的 Material Design 3 色彩 Token,可直接在你的 CSS 中引用,颜色会随用户选择的主题色和亮/暗模式自动变化

亮色模式变量

变量名用途示例值(默认紫色主题)
--md-primary主色(按钮、强调)#7D5260
--md-on-primary主色上的文字#FFFFFF
--md-primary-container主色容器背景#FFD8E4
--md-on-primary-container主色容器上的文字#21005D
--md-secondary次要色#625B71
--md-secondary-container次要色容器背景#E8DEF8
--md-tertiary第三色#7D5260
--md-surface页面/卡片背景#FFFBFE
--md-surface-container-lowest最低层容器#FFFFFF
--md-surface-container-low低层容器(卡片)#F7F2FA
--md-surface-container标准容器#F3EDF7
--md-surface-container-high高层容器#ECE6F0
--md-surface-container-highest最高层容器#E6E0E9
--md-on-surface主要文字色#1C1B1F
--md-on-surface-variant次要文字色#49454F
--md-outline边框/分割线#79747E
--md-outline-variant浅色边框#CAC4D0
--md-error错误色#B3261E
--md-error-container错误容器背景#F9DEDC

暗色模式变量(data-theme="dark" 时自动生效)

Admin Beautify 在暗色模式下会将上述变量自动替换为暗色值(如 --md-primary 在暗色下变为 #D0BCFF),无需你额外处理,直接使用变量即可。

三、实践示例

/* 一个完全兼容 Admin Beautify 的插件卡片 */
.my-plugin-card {
    background: var(--md-surface-container-low);
    border: 1px solid var(--md-outline-variant);
    border-radius: 16px;
    padding: 16px;
    color: var(--md-on-surface);
}

.my-plugin-card .title {
    color: var(--md-on-surface);
    font-weight: 600;
}

.my-plugin-card .subtitle {
    color: var(--md-on-surface-variant);
    font-size: 12px;
}

.my-plugin-card .primary-btn {
    background: var(--md-primary);
    color: var(--md-on-primary);
    border-radius: 20px;
    padding: 6px 16px;
    border: none;
    cursor: pointer;
}

/* 错误提示 */
.my-plugin-error {
    background: var(--md-error-container);
    color: var(--md-error);
    border-radius: 8px;
    padding: 8px 12px;
}

由于 Admin Beautify 在亮色/暗色切换时会同步更新所有 CSS 变量的值,上述写法自动支持暗色模式,无需额外的 [data-theme="dark"] 覆盖规则。

四、回退值(Fallback)

为了让你的插件在 未安装 Admin Beautify 的环境下也能正常显示,建议始终为 CSS 变量提供 fallback:

.my-plugin-card {
    /* var(变量, 回退值) */
    background: var(--md-surface-container-low, #f7f2fa);
    border-color: var(--md-outline-variant, #cac4d0);
    color: var(--md-on-surface, #1c1b1f);
}

[data-theme="dark"] .my-plugin-card {
    /* 无 Admin Beautify 时的暗色回退 */
    background: var(--md-surface-container-low, #211f26);
    border-color: var(--md-outline-variant, rgba(255,255,255,.12));
    color: var(--md-on-surface, #e6e1e5);
}

五、布局注意事项

注意点说明
避免固定宽度Admin Beautify 支持侧边栏和顶部导航两种布局,内容区宽度会变化,建议用 max-width + width:100%
position:fixed 弹窗如需弹窗全屏覆盖,建议将弹窗 DOM 通过 JS appendChild 挂载到 document.body,否则可能被祖先元素的 transform / overflow:hidden 限制
z-index 层级Admin Beautify 侧边栏 z-index 约为 1000,全局弹窗建议使用 z-index: 9999 以上
字体Admin Beautify 可选加载 Noto Sans SC,建议你的插件使用 font-family: inherit 跟随页面字体

🧩 兼容性脚本开发

目录:/asset/compat,此目录存放用于兼容其他 Typecho 插件的 JS 脚本文件。

工作原理

  • AdminBeautify 会自动扫描本目录下的所有 .js 文件
  • 每个脚本的 元数据(名称、简介、适用插件等)将展示在 AdminBeautify 设置页面中
  • 用户可在设置页面中 单独启用/禁用 每个脚本
  • 每个 JS 文件应自行判断当前页面是否需要执行(通过 URL、DOM 等),避免影响其他页面
  • 脚本在每次页面加载时执行一次(含 AdminBeautify AJAX 导航,见下文)
  • 用户还可以在设置面板中添加 外部 JS 文件链接 来加载额外的兼容脚本

元数据格式(必须)

每个 JS 文件的 头部注释 中必须包含以下 @tag 元数据,AdminBeautify 会解析并展示:

/**
 * @name        脚本名称
 * @description 脚本简介说明
 * @plugins     Plugin1, Plugin2
 * @version     1.0.0
 * @author      作者名
 */
标签必填说明
@name推荐脚本名称(不填则使用文件名)
@description推荐一句话功能说明
@plugins推荐适用的插件名称,多个用逗号分隔
@version可选版本号
@author可选作者

脚本结构

脚本模板

AdminBeautify 使用 AJAX 进行后台页面导航,不会完整重载页面。脚本只在首次页面加载时执行一次,若不处理 ab:pageload 事件,从其他页面 AJAX 跳转过来时修复将不会生效。

ab:pageload 事件说明:

属性说明
触发时机history.pushState 之后,document.title 和 DOM 主内容区已完成替换
e.detail.url导航目标的完整 URL
监听方式document.addEventListener('ab:pageload', fn)
注意ab:pageload 在每次 AJAX 导航后均会触发,包括 离开 目标页面时。修复函数应通过 URL 判断当前是否处于目标页面,并在离开时清理已注入的样式(保持幂等)。
/**
 * @name        MyPlugin 兼容
 * @description 修复 MyPlugin 在 AdminBeautify 下的排版问题
 * @plugins     MyPlugin
 * @version     1.0.0
 * @author      YourName
 */
(function () {
    'use strict';

    var STYLE_ID = 'ab-compat-myplugin';

    // ── 核心修复函数(幂等,可重复调用)
    function applyFix(url) {
        var isTarget = (url || '').indexOf('panel=MyPlugin') !== -1;

        if (!isTarget) {
            // 离开目标页面时清理已注入的样式
            var old = document.getElementById(STYLE_ID);
            if (old) old.remove();
            return;
        }

        // 注入修复样式(幂等:已存在则跳过)
        if (!document.getElementById(STYLE_ID)) {
            var style = document.createElement('style');
            style.id = STYLE_ID;
            style.textContent = '/* your CSS fixes */';
            document.head.appendChild(style);
        }

        // 其他 DOM 修复操作...
    }

    // ── 初始执行(首次页面加载)
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function () {
            applyFix(window.location.href);
        });
    } else {
        applyFix(window.location.href);
    }

    // ── 监听 AdminBeautify AJAX 导航事件 (必须,否则脚本不会生效)
    // e.detail.url 是导航目标 URL;此时 document.title 和 DOM 主内容区已替换完毕。
    document.addEventListener('ab:pageload', function (e) {
        var url = (e && e.detail && e.detail.url) ? e.detail.url : window.location.href;
        applyFix(url);
    });
})();

注意事项

  1. 必须判断页面:每个脚本必须先检测当前 URL 或 DOM,确定是目标页面才执行
  2. 支持 AJAX 导航:监听 ab:pageload 事件以响应 AdminBeautify 的 AJAX 页面切换(推荐)
  3. 保持幂等:修复函数可能被多次调用(首次加载 + 每次 AJAX 导航),需避免重复注入;离开目标页面时应清理已注入的样式
  4. 使用 IIFE:用 (function(){ ... })() 包裹,避免全局变量污染
  5. 使用 'use strict':启用严格模式
  6. 兼容暗色模式:如有样式修复,注意处理 [data-theme="dark"]
  7. 可用 CSS 变量:AdminBeautify 已定义大量 MD3 CSS 变量,如 --md-primary--md-surface

🔌 横幅通知API

AdminBeautify 提供了供第三方 Typecho 插件直接调用的 JavaScript 公开 API,无需自行实现通知样式。

AdminBeautify.showNotice(message, type?, duration?)

在页面顶部显示一条 MD3 Snackbar 风格的横幅通知。

参数类型默认值说明
messagestring必填通知文本内容
typestring'info'通知类型:'info' \ 'success' \ 'warn' \ 'error'
durationnumber5000自动消失时间(毫秒);传 0 则只能点击关闭

调用前提: AdminBeautify 插件已启用,且页面已加载 AdminBeautify.min.v*.js

示例:

// 1. 普通提示(默认 info 风格,5 秒后消失)
AdminBeautify.showNotice('数据已同步');

// 2. 成功提示
AdminBeautify.showNotice('设置保存成功', 'success');

// 3. 错误提示,显示 8 秒
AdminBeautify.showNotice('连接服务器失败,请稍后重试', 'error', 8000);

// 4. 持久通知(不自动消失,需用户点击关闭)
AdminBeautify.showNotice('后台任务正在运行…', 'info', 0);

在 PHP 插件中注入调用:

// 在插件的 renderFooter / action / panel 等方法中输出 inline script
echo '<script>
(function () {
    function doNotice() {
        if (window.AdminBeautify && typeof AdminBeautify.showNotice === "function") {
            AdminBeautify.showNotice("' . $message . '", "success");
        }
    }
    // 若 AB 已初始化则直接调用,否则等待 ab:pageload 事件
    if (document.readyState !== "loading" && window.AdminBeautify) {
        doNotice();
    } else {
        document.addEventListener("ab:pageload", doNotice, { once: true });
        document.addEventListener("DOMContentLoaded", doNotice, { once: true });
    }
})();
</script>';
注意: 此 API 需要 AdminBeautify ≥ v2.1.26。调用前请通过 typeof AdminBeautify !== "undefined" 判断可用性。

🔌 对话框API

概述

AdminBeautify (AB) 在 interceptNativeDialogs() 中覆写了 window.alertwindow.confirmwindow.prompt,将浏览器原生阻塞式对话框替换为 Material Design 3 风格的异步弹窗。

同步→异步断裂问题

// 浏览器原生:confirm() 阻塞执行,等用户操作后返回 true/false
if (!confirm('确定删除?')) return;   // ✅ 正常
doDelete();

// AB 劫持后:confirm() 立即返回 false,然后才弹 MD3 对话框
if (!confirm('确定删除?')) return;   // ❌ 永远走 return
doDelete();
方法原生行为AB 劫持后
alert(msg)阻塞,返回 undefined非阻塞,返回 undefined
confirm(msg)阻塞,返回 true / false立即返回 false
prompt(msg, def)阻塞,返回 string / null立即返回 null

公开 API(v2.1.33+)

AB 提供了三个 Promise 化方法,直接调用内部 _open(),无需全局变量中转:

AdminBeautify.alert(message)

@param  {string} message  消息文本(支持 \n,首行作标题)
@return {Promise<void>}   用户点击"好的"后 resolve

AdminBeautify.confirm(message)

@param  {string} message  消息文本
@return {Promise<boolean>} 确定→true, 取消/ESC/点遮罩→false

AdminBeautify.prompt(message [, defaultVal])

@param  {string} message    消息文本
@param  {string} defaultVal 输入框默认值(可选)
@return {Promise<string|null>} 确定→输入值, 取消→null

使用示例

// confirm
AdminBeautify.confirm('确定删除?\n此操作不可撤销。').then(function(ok) {
    if (!ok) return;
    doDelete();
});

// prompt
AdminBeautify.prompt('输入新名称:', '默认值').then(function(name) {
    if (!name || !name.trim()) return;
    rename(name.trim());
});

// alert 链式
AdminBeautify.alert('保存成功').then(function() {
    location.reload();
});

原生引用

如需绕过 AB 弹窗,直接使用浏览器原生对话框:

AdminBeautify.nativeAlert(msg);
AdminBeautify.nativeConfirm(msg);   // 返回 true/false(同步阻塞)
AdminBeautify.nativePrompt(msg, def); // 返回 string/null(同步阻塞)

兼容旧版AB-Admin / 无AB-Admin环境

若需同时兼容新旧版本 AB 和无 AB 环境,可用三级降级封装:

function myDialog(type, msg, defVal) {
    var AB = window.AdminBeautify;
    // AB v2.1.33+ 公开 API
    if (AB && typeof AB[type] === 'function') {
        return AB[type](msg, defVal);
    }
    return new Promise(function(resolve) {
        if (!AB) {
            // 无 AB,原生同步
            if (type === 'confirm')    resolve(confirm(msg));
            else if (type === 'prompt') resolve(prompt(msg, defVal));
            else { alert(msg); resolve(); }
            return;
        }
        // 旧版 AB,全局回调
        if (type === 'confirm') {
            window._abPendingConfirm = function(r) { resolve(!!r); };
            window.confirm(msg);
        } else if (type === 'prompt') {
            window._abPendingPrompt = function(r) { resolve(r); };
            window.prompt(msg, defVal || '');
        } else {
            window.alert(msg);
            resolve();
        }
    });
}

内部机制

window.confirm(msg)
    │
    ├─ _parseMsg(msg)      → { title: 第一行, body: 其余行 }
    ├─ _open({ type:'confirm', title, message, onConfirm, onCancel })
    │       │
    │       ├─ 创建 #ab-dialog-overlay + #ab-dialog-box
    │       ├─ WAAPI 入场动画 (scale 0.85→1 / mobile: translateY)
    │       ├─ 绑定 确定/取消/ESC/Enter/点击遮罩
    │       └─ 用户操作 → 退场动画 → 调用 onConfirm(value) 或 onCancel()
    │
    └─ return false  ← 同步立即返回,不等待用户

异步回调通道

AB 暴露了两个全局变量作为回调桥梁:

// confirm 的异步结果
window._abPendingConfirm = function(result) { /* true 或 false */ };

// prompt 的异步结果
window._abPendingPrompt = function(result) { /* string 或 null */ };

AB 的 window.confirm 覆写内部:

window.confirm = function(message) {
    var p = _parseMsg(message);
    _open({
        type: 'confirm', title: p.title, message: p.body,
        onConfirm: function() {
            if (window._abPendingConfirm) {
                var fn = window._abPendingConfirm;
                window._abPendingConfirm = null;
                fn(true);
            }
        },
        onCancel: function() {
            if (window._abPendingConfirm) {
                var fn = window._abPendingConfirm;
                window._abPendingConfirm = null;
                fn(false);
            }
        }
    });
    return false;  // ← 问题根源
};

window.prompt 同理,内部调用 _abPendingPrompt(value)_abPendingPrompt(null)

_parseMsg 消息格式

function _parseMsg(message) {
    var parts = ('' + message).split('\n');
    return { title: parts[0], body: parts.slice(1).join('\n') };
}

第一个 \n 前 → 对话框标题;之后内容 → 对话框正文。

附录:_parseMsg 消息格式规范

"标题文字\n正文内容第一行\n正文内容第二行"
部分在 AB 对话框中对应 DOM
\n 之前标题(粗体 + 图标)#ab-dialog-title
\n 之后正文#ab-dialog-msg
\n全部作为标题,无正文#ab-dialog-title

附录:AB 对话框 DOM 结构

#ab-dialog-overlay
└── #ab-dialog-box
    ├── #ab-dialog-host          "🌐 hostname"
    ├── #ab-dialog-title         图标 + 标题
    ├── #ab-dialog-msg           正文(可选)
    ├── #ab-dialog-input-wrap    输入框(仅 prompt)
    │   └── #ab-dialog-input
    └── #ab-dialog-actions
        ├── #ab-dialog-cancel    取消按钮(confirm/prompt)
        └── #ab-dialog-confirm   确定/好的 按钮

附录:AB 对话框键盘快捷键

按键行为
Enter等同点击「确定」按钮
Escapeconfirm/prompt → 等同「取消」;alert → 等同「好的」

🔗 相关链接

🐛 问题反馈
🌟 GitHub 仓库

最后编辑于: 2026 年 04 月 07 日
返回文章列表 打赏
本页链接的二维码
打赏二维码
添加新评论

已有 2 条评论
  1. xh xh        
    都开源了还加密js吗
    1. LHL LHL        
      @xh后续可能变成收费插件,然后现在加密主要是现在基本日更
    2. LHL LHL        
      @xh也没怎么加密,就混淆+base64编码
  2. 蛋蛋之家 蛋蛋之家        
    我是1.2.1,测试大部分兼容,唯一的一点就是导航栏CSS有的问题,黑乎乎的,不过我另外开了SimpleAdmin,结合的挺好的
    1. LHL LHL        
      @蛋蛋之家1.2.1和1.3.0导航栏的布局不一致导致的兼容性问题