widgetsAB Cards
insights

Blog Helper · 小程序动态

小程序(免开发)一键设置心情状态、同步微信运动、快速发布说说|时光机|碎语|日记|心情
verified最低支持版本
AB Admin ≥ 2.1.50
person作者
Chrison
event收录时间
2026-09-21 19:37
update最近更新
2026-09-22 20:00
visibility浏览 / 截图
5 次 · 123.9 KB

image效果截图

点击放大
Blog Helper · 小程序动态 截图

code配置代码

这张卡片没有 HTML 内容
(function () {
  var KEY = 'bloghelper-stats';            // 缓存键(无 card- 前缀)
  var TTL = 60 * 1000;                     // 缓存 1 分钟
  var OPTS = { scope: 'site' };            // 全站共享(后台各管理员看到同一份数据)
  var body = card.querySelector('.ab-custom-body');

  // —— 当天 0 点(用于"今日"统计)——
  var d = new Date();
  var p2 = function (n) { return (n < 10 ? '0' : '') + n; };
  var todayDate = d.getFullYear() + '-' + p2(d.getMonth() + 1) + '-' + p2(d.getDate());
  var todayStart = todayDate + ' 00:00:00';   // DATETIME 列(log 表)用
  var todayUnix = Math.floor(new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime() / 1000); // int 列(contents)用

  // —— 时间显示:int 时间戳 / DATETIME 字符串 → "MM-DD HH:mm"(自行实现,不依赖 format API 的入参格式)——
  function fmt(t) {
    try {
      var dt = (/^\d+$/.test(String(t))) ? new Date(Number(t) * 1000) : new Date(String(t).replace(' ', 'T'));
      if (isNaN(dt.getTime())) { return String(t || ''); }
      return p2(dt.getMonth() + 1) + '-' + p2(dt.getDate()) + ' ' + p2(dt.getHours()) + ':' + p2(dt.getMinutes());
    } catch (e) { return String(t || ''); }
  }

  // —— 骨架:4 个指标磁贴 + 动态清单容器 + 缓存提示 ——
  var tiles = ab.ui.metrics([
    { icon: 'forum',          label: '说说总数',     value: '—', skeleton: true },
    { icon: 'today',          label: '今日说说',     value: '—', skeleton: true },
    { icon: 'directions_run', label: '最新步数',     value: '—', skeleton: true },
    { icon: 'bolt',           label: '今日运动打卡', value: '—', skeleton: true }
  ], { cols: 2 });

  var listBox = ab.ui.el('div');

  ab.ui.mount(body, [
    tiles,
    listBox,
    ab.ui.notice({ icon: 'info', text: '数据缓存 1 分钟,点「重新读取」立即刷新', tone: 'info' })
  ]);

  // 卡片底部入口:跳插件设置页(本地已验证的地址)
  ab.ui.footer(card, { text: '插件设置', icon: 'settings', href: 'options-plugin.php?config=BlogHelper' });

  // —— 渲染:官方用法 tiles.children + setValue 逐项刷新 ——
  function render(s) {
    var t = tiles.children;
    ab.ui.setValue(t[0], ab.ui.num(s.talkTotal));
    ab.ui.setValue(t[1], ab.ui.num(s.talkToday));
    ab.ui.setValue(t[2], s.steps === null ? '—' : ab.ui.num(s.steps));
    ab.ui.setValue(t[3], ab.ui.num(s.pushToday));

    var rows = [];
    if (s.talkLast) {
      rows.push({ icon: 'edit', label: s.talkLast.title || '未命名', value: fmt(s.talkLast.created), hint: '最新说说' });
    }
    if (s.moodName) {
      rows.push({ icon: 'mood', label: s.moodName, value: fmt(s.moodTime), hint: '最新心情' });
    }
    if (s.steps !== null) {
      rows.push({ icon: 'directions_run', label: ab.ui.num(s.steps) + ' 步', value: s.stepsDate || '', hint: '最新步数' });
    }
    ab.ui.mount(listBox, rows.length
      ? ab.ui.rows(rows)
      : ab.ui.empty({ icon: 'inbox', text: '还没有小程序推送记录', hint: '在小程序里发射一次就会出现在这里' }));
  }

  // —— 取数(全部只读:ab.db.read 官方示例同款 contents 表 + 插件自有三表)——
  function fetchCounts() {
    return Promise.all([
      // 说说文章:正文含 storys 排版标记 chrison_grid_(插件发布说说时写入的特有标记)
      ab.db.read('contents', {
        columns: ['cid', 'title', 'created'],
        where: [{ field: 'text', op: 'LIKE', value: '%chrison_grid_%' }],
        orderBy: { field: 'created', dir: 'desc' },
        limit: 200
      }),
      // 今日说说
      ab.db.read('contents', {
        columns: ['cid'],
        where: [
          { field: 'text', op: 'LIKE', value: '%chrison_grid_%' },
          { field: 'created', op: '>=', value: todayUnix }
        ],
        limit: 200
      }),
      // 最新步数
      ab.db.read('blog_helper_wechat', {
        columns: ['steps', 'step_date'],
        orderBy: { field: 'created', dir: 'desc' },
        limit: 1
      }),
      // 最新心情
      ab.db.read('blog_helper_status', {
        columns: ['emoji_name', 'created'],
        orderBy: { field: 'created', dir: 'desc' },
        limit: 1
      }),
      // 今日运动打卡次数(推送成功记录)
      ab.db.read('blog_helper_log', {
        columns: ['id'],
        where: [
          { field: 'action', op: '=', value: 'steps' },
          { field: 'result', op: '=', value: 1 },
          { field: 'created', op: '>=', value: todayStart }
        ],
        limit: 200
      })
    ]).then(function (rs) {
      var talks = rs[0].rows || [];
      var wechat = (rs[2].rows || [])[0] || null;
      var mood = (rs[3].rows || [])[0] || null;
      return {
        talkTotal: talks.length,
        talkToday: (rs[1].rows || []).length,
        talkLast: talks[0] || null,
        steps: wechat ? Number(wechat.steps) : null,
        stepsDate: wechat ? wechat.step_date : '',
        moodName: mood ? mood.emoji_name : '',
        moodTime: mood ? mood.created : '',
        pushToday: (rs[4].rows || []).length
      };
    });
  }

  // —— 加载(getCached:1 分钟内切页回来不重复查询)——
  function load() {
    ab.data.getCached(KEY, TTL, fetchCounts, OPTS).then(function (s) {
      render(s);
    }).catch(function (e) {
      ab.ui.setValue(tiles.children[0], '—');
      ab.ui.setValue(tiles.children[1], '—');
      ab.ui.setValue(tiles.children[2], '—');
      ab.ui.setValue(tiles.children[3], '—');
      ab.ui.mount(listBox, ab.ui.empty({ icon: 'error', text: '数据加载失败', hint: (e && e.message) || '' }));
      ab.ui.notice({ text: 'Blog Helper 数据加载失败:' + ((e && e.message) || '未知错误'), tone: 'danger' });
    });
  }

  load();

  // 手动刷新(清缓存重拉)
  ab.ui.mount(body, ab.ui.actions([
    ab.ui.button({ text: '重新读取', icon: 'refresh', tone: 'info', onClick: function () {
      ab.data.clearCached(KEY, OPTS);
      load();
    }})
  ]));
})();

download_for_offline如何导入

Typecho 后台 → 控制台 → 插件 → AB Admin(AdminBeautify)→「概要页卡片设置」
找到 自定义卡片 区域,打开总开关(默认关闭)
点 添加卡片,按下表填写:
图标 insights
卡片标题 Blog Helper · 小程序动态
HTML 内容 (留空)
JavaScript 见上方「JavaScript」标签页
点右下角 保存设置,回到概要页即可看到卡片(顺序可在排序列表里拖动调整)

shield安全信息

verified_user 扫描通过
database数据来源
databaseab.api(contents)databaseab.api(blog_helper_wechat)databaseab.api(blog_helper_status)databaseab.api(blog_helper_log)
widgets使用的内置组件
ab.ui.metricsab.ui.elab.ui.mountab.ui.noticeab.ui.footerab.ui.setValueab.ui.numab.ui.rowsab.ui.emptyab.ui.actionsab.ui.button
warning
卡片 JavaScript 会在你的后台页面里执行。本站已做敏感信息、站外请求与越权操作的自动扫描 + 人工审核,但仍请在使用前自行阅读上方代码;发现可疑卡片请到 GitHub 反馈。