{"id":137,"date":"2026-09-20T22:07:31","date_gmt":"2026-09-20T14:07:31","guid":{"rendered":"https:\/\/www.fmor.cn\/index.php\/sstv-%e8%af%81%e4%b9%a6%e9%aa%8c%e7%9c%9f\/"},"modified":"2026-09-25T22:32:30","modified_gmt":"2026-09-25T14:32:30","slug":"sstv-verify","status":"publish","type":"page","link":"https:\/\/www.fmor.cn\/index.php\/sstv-verify\/","title":{"rendered":"\u661f\u9891\u5f52\u822a\u96c6\u7fa4SSTV\u8bc1\u4e66\u9a8c\u771f"},"content":{"rendered":"\n<style>\n  :root{\n    --bg:#e9eef3;\n    --card:#ffffff;\n    --ink:#1b2b38;\n    --muted:#5d6c79;\n    --line:#dbe2e9;\n    --navy-1:#0c2436;\n    --navy-2:#133b54;\n    --teal:#0e7c66;\n    --teal-dark:#0a5f4e;\n    --green:#178a55;\n    --green-bg:#effaf4;\n    --green-line:#b7e5cc;\n    --red:#c0392b;\n    --red-bg:#fdf3f2;\n    --red-line:#efc3bc;\n    --amber:#e09b13;\n    --amber-bg:#fff8ea;\n    --amber-line:#f0d9a2;\n    --radius:16px;\n  }\n  *{box-sizing:border-box}\n  body{\n    margin:0;\n    font-family:\"PingFang SC\",\"Microsoft YaHei\",\"Noto Sans CJK SC\",\"Source Han Sans SC\",system-ui,-apple-system,sans-serif;\n    background:var(--bg);\n    color:var(--ink);\n    line-height:1.65;\n    -webkit-text-size-adjust:100%;\n  }\n  .sstv-app{max-width:580px;margin:0 auto;padding:28px 16px 44px}\n\n  \/* ===== \u9876\u90e8\u6a2a\u5e45 ===== *\/\n  .sstv-head{\n    position:relative;\n    overflow:hidden;\n    background:linear-gradient(158deg,var(--navy-1) 0%,var(--navy-2) 62%,#17607e 100%);\n    border-radius:var(--radius);\n    padding:26px 24px 22px;\n    color:#eaf4f7;\n  }\n  .sstv-head .waves{position:absolute;right:12px;top:12px;opacity:.9;pointer-events:none}\n  .sstv-head .kicker{\n    display:inline-flex;align-items:center;gap:8px;\n    font-size:13px;letter-spacing:2px;color:#9fd9c8;margin:0 0 10px;\n  }\n  .sstv-head .kicker::before{content:\"\";width:22px;height:2px;background:var(--amber);border-radius:2px}\n  .sstv-head h1{margin:0;font-size:23px;font-weight:700;letter-spacing:.5px}\n  .sstv-head p{margin:8px 0 0;font-size:14px;color:#cfe2ea;max-width:340px}\n\n  \/* ===== \u8868\u5355\u5361\u7247 ===== *\/\n  .sstv-card{\n    background:var(--card);\n    border:1px solid var(--line);\n    border-radius:var(--radius);\n    padding:22px 22px 24px;\n    margin-top:16px;\n    box-shadow:0 2px 12px rgba(12,36,54,.06);\n  }\n  .sstv-card label{display:block;font-size:13px;font-weight:600;color:var(--muted);margin:16px 0 6px}\n  .sstv-card label:first-child{margin-top:0}\n  .sstv-card input{\n    width:100%;\n    padding:11px 12px;\n    font-size:16px;\n    color:var(--ink);\n    background:#fbfcfd;\n    border:1.5px solid var(--line);\n    border-radius:10px;\n    transition:border-color .15s, box-shadow .15s;\n  }\n  .sstv-card input::placeholder{color:#9aa8b4}\n  .sstv-card input:focus{outline:none;border-color:var(--teal);box-shadow:0 0 0 3px rgba(14,124,102,.14)}\n  .sstv-card .mono,.dl .mono{font-family:\"Consolas\",\"Menlo\",\"SF Mono\",monospace;letter-spacing:.5px}\n  .sstv-btn{\n    display:block;width:100%;\n    margin-top:20px;padding:12px 16px;\n    font-size:16px;font-weight:600;color:#fff;\n    background:var(--teal);border:none;border-radius:10px;\n    cursor:pointer;\n    transition:background .15s, transform .05s;\n  }\n  .sstv-btn:hover{background:var(--teal-dark)}\n  .sstv-btn:active{transform:scale(.99)}\n  .sstv-btn:focus-visible{outline:3px solid rgba(14,124,102,.4);outline-offset:2px}\n  .sstv-hint{margin-top:12px;font-size:13px;color:#98a6b2;text-align:center}\n\n  \/* ===== \u7ed3\u679c\u533a ===== *\/\n  .sstv-result{\n    margin-top:16px;\n    border-radius:var(--radius);\n    padding:20px 22px;\n    border:1.5px solid var(--line);\n    background:#fafbfc;\n  }\n  .sstv-result[hidden]{display:none}\n  .sstv-result.good{background:var(--green-bg);border-color:var(--green-line)}\n  .sstv-result.bad{background:var(--red-bg);border-color:var(--red-line)}\n  .sstv-result.revoked{background:var(--amber-bg);border-color:var(--amber-line)}\n  .sstv-result .s-line{display:flex;align-items:center;gap:10px;font-size:17px;font-weight:700}\n  .sstv-result .s-line .ic{display:inline-flex;flex-shrink:0}\n  .sstv-result .s-line .ic svg{display:block}\n  .sstv-result p{margin:8px 0 0;font-size:13.5px;color:#44525f}\n  .sstv-result.good .s-line{color:var(--green)}\n  .sstv-result.bad .s-line{color:var(--red)}\n  .sstv-result.revoked .s-line{color:#a06a00}\n  .dl{\n    display:grid;\n    grid-template-columns:92px 1fr;\n    gap:9px 14px;\n    margin-top:14px;\n    padding-top:14px;\n    border-top:1px dashed rgba(27,43,56,.14);\n  }\n  .dl dt{font-size:13px;color:var(--muted)}\n  .dl dd{margin:0;font-size:15px;word-break:break-all}\n  .badge{\n    display:inline-flex;align-items:center;gap:6px;\n    padding:2px 12px;border-radius:999px;\n    font-size:13px;font-weight:600;color:#fff;\n  }\n  .badge.good{background:var(--green)}\n  .badge.revoked{background:var(--amber);color:#5b4200}\n  .sstv-foot{margin-top:24px;text-align:center;font-size:13px;color:#8b99a5}\n  .sstv-foot .org{font-weight:600;color:#6b7a87}\n\n  \/* ===== \u540e\u53f0\u7ba1\u7406 ===== *\/\n  .sstv-admin-entry{color:#8b99a5;text-decoration:none;font-size:13px;transition:color .15s}\n  .sstv-admin-entry:hover{color:var(--teal);text-decoration:underline}\n  .sstv-call{margin-top:6px;font-size:13px;color:#7c8b98}\n  .admin-view{padding:22px 22px 24px}\n  .admin-title{margin:0 0 2px;font-size:19px;font-weight:700}\n  .admin-tip{margin:4px 0 14px;font-size:13px;color:var(--muted)}\n  .adm-msg{font-size:14px;margin-top:12px}\n  .adm-msg.err{color:var(--red)}\n  .adm-msg.ok{color:var(--green)}\n  .adm-table-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:10px;margin:4px 0 6px}\n  .adm-table{width:100%;border-collapse:collapse;font-size:13.5px;table-layout:fixed}\n  .adm-table th,.adm-table td{padding:8px 10px;text-align:left;border-bottom:1px solid var(--line);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n  .adm-table th{background:#f2f6f9;font-weight:600;color:var(--muted)}\n  .adm-table tbody tr:last-child td{border-bottom:none}\n  .adm-empty{color:#98a6b2;text-align:center}\n  .adm-del{font-size:12.5px;padding:3px 10px;border:1px solid var(--line);border-radius:8px;background:#fff;color:#7c8b98;cursor:pointer;transition:color .15s,border-color .15s,background .15s}\n  .adm-del:hover{border-color:var(--red);color:var(--red)}\n  .adm-del.danger{background:var(--red);border-color:var(--red);color:#fff}\n  .adm-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}\n  .adm-grid label{display:block;font-size:13px;font-weight:600;color:var(--muted);margin:14px 0 6px}\n  .adm-grid input{width:100%;padding:10px 12px;font-size:16px;color:var(--ink);background:#fbfcfd;border:1.5px solid var(--line);border-radius:10px}\n  .adm-grid input:focus{outline:none;border-color:var(--teal);box-shadow:0 0 0 3px rgba(14,124,102,.14)}\n  .adm-export{width:100%;min-height:140px;padding:10px 12px;font-family:\"Consolas\",\"Menlo\",\"SF Mono\",monospace;font-size:12.5px;line-height:1.5;color:#22404f;background:#fbfcfd;border:1.5px solid var(--line);border-radius:10px;resize:vertical}\n  .adm-export:focus{outline:none;border-color:var(--teal)}\n  .export-tip{margin:18px 0 8px}\n  .admin-back{margin-top:16px;font-size:14px}\n  .admin-back a{color:var(--teal);text-decoration:none}\n  .admin-back a:hover{text-decoration:underline}\n\n  @media (max-width:420px){\n    .sstv-head{padding:22px 18px 18px}\n    .sstv-head .waves{transform:scale(.82);transform-origin:top right}\n    .dl{grid-template-columns:80px 1fr}\n    .adm-grid{grid-template-columns:1fr}\n    .adm-table{min-width:600px}\n  }\n  @media (prefers-reduced-motion:reduce){\n    *{transition:none!important;scroll-behavior:auto!important}\n  }\n\n  \/* ===== \u4e3b\u9898\u8986\u76d6\u9632\u62a4\uff1a\u907f\u514d WordPress \u4e3b\u9898\u6837\u5f0f\u538b\u6389\u672c\u9875\u5173\u952e\u5916\u89c2 ===== *\/\n  .sstv-app{background:var(--bg) !important}\n  .entry-hero.page-hero-section{display:none !important}\n  .sstv-head{background:linear-gradient(158deg,var(--navy-1) 0%,var(--navy-2) 62%,#17607e 100%) !important;color:#eaf4f7 !important}\n  .sstv-head h1{color:#ffffff !important}\n  .sstv-head .kicker{color:#9fd9c8 !important}\n  .sstv-head p{color:#cfe2ea !important}\n  .sstv-card{background:var(--card) !important;border:1px solid var(--line) !important}\n  .sstv-btn{background:var(--teal) !important;color:#ffffff !important;border:none !important}\n  .sstv-btn:hover{background:var(--teal-dark) !important}\n  .sstv-app input.mono{background:#ffffff !important;border:1px solid var(--line) !important;color:var(--ink) !important;font-family:ui-monospace,Consolas,Menlo,monospace !important}\n  .sstv-app label{color:var(--ink) !important}\n<\/style>\n\n<!-- ===== \u9875\u9762\u6b63\u6587 ===== -->\n<div class=\"sstv-app\">\n  <header class=\"sstv-head\">\n    <svg class=\"waves\" width=\"108\" height=\"88\" viewBox=\"0 0 108 88\" fill=\"none\" aria-hidden=\"true\">\n      <path d=\"M6 60 a44 44 0 0 1 88 0\" stroke=\"#57d3b0\" stroke-width=\"3.5\" stroke-linecap=\"round\"\/>\n      <path d=\"M21 60 a29 29 0 0 1 58 0\" stroke=\"#9fe8d2\" stroke-width=\"3.5\" stroke-linecap=\"round\"\/>\n      <path d=\"M35 60 a15 15 0 0 1 30 0\" stroke=\"#eafbf4\" stroke-width=\"3.5\" stroke-linecap=\"round\"\/>\n      <rect x=\"50\" y=\"34\" width=\"8\" height=\"13\" rx=\"2\" fill=\"#e0a92e\"\/>\n      <rect x=\"44\" y=\"26\" width=\"20\" height=\"4\" rx=\"2\" fill=\"#e0a92e\"\/>\n    <\/svg>\n    <p class=\"kicker\">AMATEUR RADIO \u00b7 SSTV<\/p>\n    <h1>SSTV \u6d3b\u52a8\u8bc1\u4e66\u9a8c\u771f<\/h1>\n    <p>\u8f93\u5165\u8bc1\u4e66\u4e0a\u7684\u547c\u53f7\u4e0e\u8bc1\u4e66\u7f16\u53f7\uff0c\u67e5\u9a8c\u8be5\u8bc1\u4e66\u662f\u5426\u7531\u4e3b\u529e\u65b9\u771f\u5b9e\u9881\u53d1\u3002<\/p>\n  <\/header>\n\n  <section class=\"sstv-card\" id=\"verifyArea\" aria-label=\"\u8bc1\u4e66\u67e5\u9a8c\">\n    <form id=\"verifyForm\" novalidate>\n      <label for=\"callsign\">\u547c\u53f7<\/label>\n      <input class=\"mono\" id=\"callsign\" name=\"callsign\" type=\"text\"\n             placeholder=\"\u4f8b\u5982 BH3NTC\" maxlength=\"16\"\n             autocomplete=\"off\" autocapitalize=\"characters\" spellcheck=\"false\" required>\n\n      <label for=\"certNo\">\u8bc1\u4e66\u7f16\u53f7<\/label>\n      <input class=\"mono\" id=\"certNo\" name=\"certNo\" type=\"text\"\n             maxlength=\"40\"\n             autocomplete=\"off\" spellcheck=\"false\" required>\n\n      <button class=\"sstv-btn\" id=\"verifyBtn\" type=\"submit\">\u67e5\u9a8c\u771f\u4f2a<\/button>\n    <\/form>\n    <p class=\"sstv-hint\">\u547c\u53f7\u4e0e\u8bc1\u4e66\u7f16\u53f7\u89c1\u8bc1\u4e66\u6b63\u9762<\/p>\n  <\/section>\n\n  <div id=\"result\" class=\"sstv-result\" role=\"status\" aria-live=\"polite\" hidden><\/div>\n\n  <!-- ===== \u540e\u53f0\u7ba1\u7406\uff08\u70b9\u51fb\u9875\u811a\u300c\u540e\u53f0\u7ba1\u7406\u300d\u8fdb\u5165\uff09 ===== -->\n  <section class=\"sstv-card admin-view\" id=\"adminView\" aria-label=\"\u540e\u53f0\u7ba1\u7406\" hidden>\n    <h2 class=\"admin-title\">\u540e\u53f0\u7ba1\u7406 \u00b7 \u8bc1\u4e66\u767b\u8bb0<\/h2>\n\n    <div id=\"loginBox\">\n      <p class=\"admin-tip\">\u8bf7\u8f93\u5165\u7ba1\u7406\u5458\u8d26\u53f7\u548c\u5bc6\u7801\u767b\u5f55<\/p>\n      <label for=\"admUser\">\u7528\u6237\u540d<\/label>\n      <input class=\"mono\" id=\"admUser\" type=\"text\" maxlength=\"20\" autocomplete=\"username\" spellcheck=\"false\">\n      <label for=\"admPass\">\u5bc6\u7801<\/label>\n      <input class=\"mono\" id=\"admPass\" type=\"password\" maxlength=\"40\" autocomplete=\"current-password\">\n      <button class=\"sstv-btn\" id=\"admLogin\" type=\"button\">\u767b\u5f55<\/button>\n      <p class=\"adm-msg err\" id=\"admErr\" hidden>\u7528\u6237\u540d\u6216\u5bc6\u7801\u4e0d\u6b63\u786e\uff0c\u8bf7\u91cd\u8bd5\u3002<\/p>\n    <\/div>\n\n    <div id=\"panelBox\" hidden>\n      <p class=\"admin-tip\">\u767b\u8bb0\u6216\u5220\u9664\u8bb0\u5f55\u540e\u7acb\u5373\u4fdd\u5b58\u5230\u670d\u52a1\u5668\uff0c\u7535\u8111 \/ \u624b\u673a\u7b49\u6240\u6709\u8bbe\u5907\u90fd\u80fd\u67e5\u5230\u3002\u4e0b\u65b9\u5bfc\u51fa\u6846\u4ec5\u4f5c\u5907\u4efd\u3002<\/p>\n      <div class=\"adm-table-wrap\">\n        <table class=\"adm-table\">\n          <colgroup><col style=\"width:17%\"><col style=\"width:22%\"><col style=\"width:12%\"><col style=\"width:21%\"><col style=\"width:11%\"><col style=\"width:17%\"><\/colgroup>\n          <thead><tr><th>\u547c\u53f7<\/th><th>\u8bc1\u4e66\u7f16\u53f7<\/th><th>\u59d3\u540d<\/th><th>\u6d3b\u52a8<\/th><th>\u65e5\u671f<\/th><th>\u64cd\u4f5c<\/th><\/tr><\/thead>\n          <tbody id=\"admTbody\"><\/tbody>\n        <\/table>\n      <\/div>\n      <form id=\"addForm\" novalidate>\n        <div class=\"adm-grid\">\n          <div><label for=\"nCallsign\">\u547c\u53f7 *<\/label><input class=\"mono\" id=\"nCallsign\" maxlength=\"16\" autocomplete=\"off\" spellcheck=\"false\"><\/div>\n          <div><label for=\"nCertNo\">\u8bc1\u4e66\u7f16\u53f7 *<\/label><input class=\"mono\" id=\"nCertNo\" maxlength=\"40\" autocomplete=\"off\" spellcheck=\"false\"><\/div>\n          <div><label for=\"nName\">\u59d3\u540d\uff08\u9009\u586b\uff09<\/label><input id=\"nName\" maxlength=\"40\"><\/div>\n          <div><label for=\"nEvent\">\u6d3b\u52a8\uff08\u9009\u586b\uff09<\/label><input id=\"nEvent\" maxlength=\"60\"><\/div>\n          <div><label for=\"nDate\">\u65e5\u671f\uff08\u9009\u586b\uff09<\/label><input id=\"nDate\" maxlength=\"10\" placeholder=\"YYYY-MM-DD\"><\/div>\n          <div><label for=\"nNote\">\u5907\u6ce8\uff08\u9009\u586b\uff09<\/label><input id=\"nNote\" maxlength=\"80\"><\/div>\n        <\/div>\n        <button class=\"sstv-btn\" id=\"addBtn\" type=\"submit\">\u6dfb\u52a0\u8bb0\u5f55<\/button>\n        <p class=\"adm-msg ok\" id=\"addMsg\" hidden><\/p>\n        <p class=\"adm-msg err\" id=\"addErr\" hidden><\/p>\n      <\/form>\n      <p class=\"admin-tip export-tip\">\u5907\u4efd \/ \u5bfc\u51fa\u5f53\u524d\u8bb0\u5f55\uff08\u53ef\u9009\uff09\uff1a<\/p>\n      <textarea class=\"adm-export\" id=\"admExport\" readonly spellcheck=\"false\"><\/textarea>\n      <button class=\"sstv-btn\" id=\"admCopy\" type=\"button\">\u590d\u5236\u4ee3\u7801<\/button>\n      <p class=\"adm-msg ok\" id=\"copyMsg\" hidden><\/p>\n    <\/div>\n\n    <p class=\"admin-back\"><a href=\"#verify\" id=\"backLink\">\u2190 \u8fd4\u56de\u8bc1\u4e66\u67e5\u9a8c<\/a><\/p>\n  <\/section>\n\n  <footer class=\"sstv-foot\">\n    <p>\u4e3b\u529e\uff1a<span class=\"org\">\u661f\u9891\u5f52\u822a\u96c6\u7fa4<\/span> \u00b7 \u672c\u9875\u9762\u7528\u4e8e\u8bc1\u4e66\u771f\u4f2a\u67e5\u9a8c<\/p>\n    <p><a class=\"sstv-admin-entry\" href=\"#admin\" id=\"adminLink\">\u540e\u53f0\u7ba1\u7406<\/a><\/p>\n    <p class=\"sstv-call mono\">bh3ntc<\/p>\n  <\/footer>\n<\/div>\n\n<script>\n\/* ============================================================\n   \u8bc1\u4e66\u6570\u636e\u7edf\u4e00\u4fdd\u5b58\u5728\u7f51\u7ad9\u670d\u52a1\u5668\uff08\u9875\u9762\u5e95\u90e8\u300c\u540e\u53f0\u7ba1\u7406\u300d\u767b\u8bb0\u5373\u53ef\uff0c\n   \u81ea\u52a8\u4fdd\u5b58\u5230\u670d\u52a1\u5668\uff0c\u7535\u8111 \/ \u624b\u673a\u7b49\u6240\u6709\u8bbe\u5907\u90fd\u80fd\u67e5\u5230\uff09\u3002\n   \u4e0b\u65b9 CERTS \u6570\u7ec4\u4ec5\u4f5c\u79bb\u7ebf\u5907\u7528\uff0c\u4e00\u822c\u4fdd\u6301\u4e3a\u7a7a\uff0c\u65e0\u9700\u624b\u52a8\u7f16\u8f91\u3002\n   ============================================================ *\/\nvar CERTS = [\n];\n\n\/* ===== \u540e\u53f0 API\uff1a\u767b\u5f55\u4e0e\u8bc1\u4e66\u6570\u636e\u5747\u7531\u670d\u52a1\u5668\u5904\u7406\uff0c\u6e90\u7801\u4e2d\u4e0d\u5b58\u653e\u4efb\u4f55\u51ed\u636e ===== *\/\nvar ADMIN_API = \"https:\/\/www.fmor.cn\/index.php\/wp-json\/sstv\/v1\/admin-login\";\nvar CERTS_API = \"https:\/\/www.fmor.cn\/index.php\/wp-json\/sstv\/v1\/certs\";\n\n\/* ===== \u67e5\u9a8c\u903b\u8f91\uff08\u4e00\u822c\u65e0\u9700\u4fee\u6539\uff09 ===== *\/\n(function () {\n  var $ = function (id) { return document.getElementById(id); };\n  var form = $(\"verifyForm\"), callsign = $(\"callsign\"), certNo = $(\"certNo\"), result = $(\"result\");\n\n  \/* URL \u53c2\u6570\u9884\u586b\uff1a?callsign=xxx \u4e0e cert=xxx\uff08\u4e8c\u7ef4\u7801\u7f51\u5740\u53ef\u5e26\u4e0a\uff0c\u626b\u7801\u81ea\u52a8\u586b\u597d\uff09 *\/\n  try {\n    var p = new URLSearchParams(window.location.search);\n    if (p.get(\"callsign\")) callsign.value = p.get(\"callsign\");\n    if (p.get(\"cert\")) certNo.value = p.get(\"cert\");\n  } catch (e) {}\n\n  \/* \u4ece\u670d\u52a1\u5668\u52a0\u8f7d\u8bc1\u4e66\u6570\u636e\uff08\u6240\u6709\u8bbe\u5907\u5171\u4eab\u540c\u4e00\u4efd\u6570\u636e\uff09 *\/\n  var loadXhr = new XMLHttpRequest();\n  loadXhr.open(\"GET\", CERTS_API);\n  loadXhr.timeout = 10000;\n  loadXhr.onreadystatechange = function () {\n    if (loadXhr.readyState !== 4) return;\n    try {\n      var d = JSON.parse(loadXhr.responseText);\n      if (d) {\n        if (d.certs) {\n          CERTS.length = 0;\n          Array.prototype.push.apply(CERTS, d.certs);\n        }\n      }\n    } catch (e) {}\n  };\n  loadXhr.send();\n\n  var norm = function (v) { return String(v == null ? \"\" : v).replace(\/^\\s+|\\s+$\/g, \"\").toUpperCase(); };\n\n  var esc = function (t) {\n    var d = document.createElement(\"div\");\n    d.textContent = (t == null ? \"\" : String(t));\n    return d.innerHTML;\n  };\n\n  var ICON_OK = '<svg width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"currentColor\" opacity=\".16\"\/><path d=\"M8 12.3l2.6 2.6 5.4-5.8\" stroke=\"currentColor\" stroke-width=\"2.4\" fill=\"none\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg>';\n  var ICON_NO = '<svg width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"currentColor\" opacity=\".16\"\/><path d=\"M9 9l6 6M15 9l-6 6\" stroke=\"currentColor\" stroke-width=\"2.4\" fill=\"none\" stroke-linecap=\"round\"\/><\/svg>';\n  var ICON_ALERT = '<svg width=\"26\" height=\"26\" viewBox=\"0 0 24 24\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"currentColor\" opacity=\".16\"\/><path d=\"M12 8v5M12 15.8v.4\" stroke=\"currentColor\" stroke-width=\"2.4\" fill=\"none\" stroke-linecap=\"round\"\/><\/svg>';\n\n  function detailsHtml(r, badgeClass, badgeText) {\n    var rows = \"\";\n    rows += '<div class=\"dl\">';\n    rows += '<dt>\u8bc1\u4e66\u7f16\u53f7<\/dt><dd class=\"mono\">' + esc(r.certNo) + '<\/dd>';\n    rows += '<dt>\u547c\u53f7<\/dt><dd class=\"mono\">' + esc(r.callsign) + '<\/dd>';\n    if (r.name)  rows += '<dt>\u59d3\u540d<\/dt><dd>' + esc(r.name) + '<\/dd>';\n    if (r.event) rows += '<dt>\u6d3b\u52a8<\/dt><dd>' + esc(r.event) + '<\/dd>';\n    if (r.date)  rows += '<dt>\u9881\u53d1\u65e5\u671f<\/dt><dd>' + esc(r.date) + '<\/dd>';\n    if (r.note)  rows += '<dt>\u5907\u6ce8<\/dt><dd>' + esc(r.note) + '<\/dd>';\n    rows += '<dt>\u9a8c\u8bc1\u72b6\u6001<\/dt><dd><span class=\"badge ' + badgeClass + '\">' + badgeText + '<\/span><\/dd>';\n    rows += '<\/div>';\n    return rows;\n  }\n\n  function show(kind, html) {\n    result.className = \"sstv-result \" + kind;\n    result.innerHTML = html;\n    result.hidden = false;\n    var reduce = false;\n    if (window.matchMedia) reduce = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n    result.scrollIntoView({ behavior: reduce ? \"auto\" : \"smooth\", block: \"nearest\" });\n  }\n\n  function verify(ev) {\n    if (ev) ev.preventDefault();\n    var c = norm(callsign.value), n = norm(certNo.value);\n\n    if (!c || !n) {\n      show(\"bad\",\n        '<div class=\"s-line\"><span class=\"ic\">' + ICON_ALERT + '<\/span>\u8bf7\u5b8c\u6574\u8f93\u5165\u547c\u53f7\u548c\u8bc1\u4e66\u7f16\u53f7<\/div>' +\n        '<p>\u4e24\u9879\u90fd\u586b\u597d\u540e\u518d\u6b21\u70b9\u51fb\u300c\u67e5\u9a8c\u771f\u4f2a\u300d\u3002<\/p>');\n      return;\n    }\n\n    var found = null;\n    for (var i = 0; i < CERTS.length; i++) {\n      var r = CERTS[i];\n      if (r) {\n        if (norm(r.callsign) === c) {\n          if (norm(r.certNo) === n) { found = r; break; }\n        }\n      }\n    }\n\n    if (!found) {\n      show(\"bad\",\n        '<div class=\"s-line\"><span class=\"ic\">' + ICON_NO + '<\/span>\u672a\u67e5\u8be2\u5230\u5339\u914d\u8bb0\u5f55<\/div>' +\n        '<p>\u8bf7\u6838\u5bf9\u8bc1\u4e66\u4e0a\u7684\u547c\u53f7\u4e0e\u8bc1\u4e66\u7f16\u53f7\u662f\u5426\u8f93\u5165\u6b63\u786e\uff08\u7f16\u53f7\u4e2d\u7684\u5b57\u6bcd\u4e0d\u533a\u5206\u5927\u5c0f\u5199\uff09\u3002' +\n        '\u82e5\u4ecd\u65e0\u6cd5\u67e5\u8be2\uff0c\u8bf7\u8054\u7cfb\u4e3b\u529e\u65b9\u6838\u5b9e\u3002<\/p>');\n      return;\n    }\n\n    if (found.revoked) {\n      show(\"revoked\",\n        '<div class=\"s-line\"><span class=\"ic\">' + ICON_ALERT + '<\/span>\u8be5\u8bc1\u4e66\u5df2\u88ab\u64a4\u9500<\/div>' +\n        '<p>\u6b64\u7f16\u53f7\u7684\u8bc1\u4e66\u66fe\u7ecf\u9881\u53d1\uff0c\u4f46\u5df2\u88ab\u4e3b\u529e\u65b9\u64a4\u9500\uff0c\u4e0d\u518d\u89c6\u4e3a\u6709\u6548\u8bc1\u4e66\u3002<\/p>' +\n        detailsHtml(found, \"revoked\", \"\u5df2\u64a4\u9500\"));\n      return;\n    }\n\n    show(\"good\",\n      '<div class=\"s-line\"><span class=\"ic\">' + ICON_OK + '<\/span>\u8bc1\u4e66\u771f\u5b9e\u6709\u6548<\/div>' +\n      '<p>\u4ee5\u4e0b\u8bb0\u5f55\u4e0e\u4e3b\u529e\u65b9\u767b\u8bb0\u4fe1\u606f\u4e00\u81f4\u3002<\/p>' +\n      detailsHtml(found, \"good\", \"\u6709\u6548\"));\n  }\n\n  form.addEventListener(\"submit\", verify);\n})();\n\n\/* ===== \u540e\u53f0\u7ba1\u7406\uff1a\u767b\u5f55\uff08\u670d\u52a1\u7aef\u6821\u9a8c\uff09 + \u767b\u8bb0\u8bc1\u4e66 + \u5bfc\u51fa\u6570\u636e ===== *\/\n(function () {\n  var $ = function (id) { return document.getElementById(id); };\n  var norm = function (v) { return String(v == null ? \"\" : v).replace(\/^\\s+|\\s+$\/g, \"\").toUpperCase(); };\n  var verifyArea = $(\"verifyArea\"), adminView = $(\"adminView\"), resultBox = $(\"result\");\n  var loginBox = $(\"loginBox\"), panelBox = $(\"panelBox\");\n  var admUser = $(\"admUser\"), admPass = $(\"admPass\"), admErr = $(\"admErr\");\n  var admTbody = $(\"admTbody\"), admExport = $(\"admExport\");\n  var addForm = $(\"addForm\"), addMsg = $(\"addMsg\"), addErr = $(\"addErr\");\n  var copyMsg = $(\"copyMsg\");\n\n  function showAdmin() {\n    verifyArea.hidden = true; resultBox.hidden = true; adminView.hidden = false;\n    loginBox.hidden = false; panelBox.hidden = true; admErr.hidden = true;\n  }\n  function showVerify() {\n    adminView.hidden = true; verifyArea.hidden = false;\n  }\n  $(\"adminLink\").addEventListener(\"click\", function (e) { e.preventDefault(); showAdmin(); location.hash = \"admin\"; });\n  $(\"backLink\").addEventListener(\"click\", function (e) { e.preventDefault(); showVerify(); location.hash = \"verify\"; });\n\n  function openPanel() {\n    loginBox.hidden = true; panelBox.hidden = false;\n    refreshCerts(function () { renderTable(); renderExport(); });\n  }\n\n  \/* \u4ece\u670d\u52a1\u5668\u5237\u65b0\u8bc1\u4e66\u6570\u636e *\/\n  function refreshCerts(done) {\n    var x = new XMLHttpRequest();\n    x.open(\"GET\", CERTS_API);\n    x.timeout = 10000;\n    x.onreadystatechange = function () {\n      if (x.readyState !== 4) return;\n      var ok = false;\n      try {\n        var d = JSON.parse(x.responseText);\n        if (d) {\n          if (d.certs) {\n            CERTS.length = 0;\n            Array.prototype.push.apply(CERTS, d.certs);\n            ok = true;\n          }\n        }\n      } catch (e2) {}\n      if (done) done(ok);\n    };\n    x.send();\n  }\n\n  function login() {\n    var u = admUser.value.trim().toUpperCase();\n    var p = admPass.value;\n    if (!u || !p) { admErr.textContent = \"\u8bf7\u8f93\u5165\u7528\u6237\u540d\u548c\u5bc6\u7801\u3002\"; admErr.hidden = false; return; }\n    admErr.hidden = true;\n    var xhr = new XMLHttpRequest();\n    xhr.open(\"POST\", ADMIN_API);\n    xhr.setRequestHeader(\"Content-Type\", \"application\/json\");\n    xhr.timeout = 10000;\n    xhr.onreadystatechange = function () {\n      if (xhr.readyState !== 4) return;\n      var ok = false;\n      try { ok = JSON.parse(xhr.responseText).ok === true; } catch (e2) { ok = false; }\n      if (ok) {\n        admErr.hidden = true;\n        openPanel();\n      } else {\n        admErr.textContent = \"\u7528\u6237\u540d\u6216\u5bc6\u7801\u4e0d\u6b63\u786e\uff0c\u8bf7\u91cd\u8bd5\u3002\";\n        admErr.hidden = false;\n      }\n    };\n    xhr.onerror = function () { admErr.textContent = \"\u7f51\u7edc\u9519\u8bef\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002\"; admErr.hidden = false; };\n    xhr.ontimeout = function () { admErr.textContent = \"\u670d\u52a1\u5668\u65e0\u54cd\u5e94\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002\"; admErr.hidden = false; };\n    xhr.send(JSON.stringify({ u: u, p: p }));\n  }\n  $(\"admLogin\").addEventListener(\"click\", login);\n  admPass.addEventListener(\"keydown\", function (e) { if (e.key === \"Enter\") login(); });\n\n  function escCell(t) {\n    var d = document.createElement(\"div\"); d.textContent = (t == null ? \"\" : String(t)); return d.innerHTML;\n  }\n\n  function renderTable() {\n    var html = \"\";\n    for (var i = 0; i < CERTS.length; i++) {\n      var r = CERTS[i];\n      html += \"<tr><td class=\\\"mono\\\">\" + escCell(r.callsign) + \"<\/td><td class=\\\"mono\\\">\" + escCell(r.certNo) +\n              \"<\/td><td>\" + escCell(r.name || \"\") + \"<\/td><td>\" + escCell(r.event || \"\") + \"<\/td><td>\" + escCell(r.date || \"\") +\n              \"<\/td><td><button class=\\\"adm-del\\\" type=\\\"button\\\" data-idx=\\\"\" + i + \"\\\">\u5220\u9664<\/button><\/td><\/tr>\";\n    }\n    admTbody.innerHTML = html || \"<tr><td colspan=\\\"6\\\" class=\\\"adm-empty\\\">\u6682\u65e0\u8bb0\u5f55\uff08\u5728\u4e0b\u65b9\u767b\u8bb0\u65b0\u8bc1\u4e66\uff09<\/td><\/tr>\";\n  }\n\n  \/* \u5220\u9664\uff1a\u70b9\u4e00\u6b21\u53d8\u300c\u786e\u8ba4\u5220\u9664\uff1f\u300d\uff0c\u518d\u70b9\u4e00\u6b21\u624d\u771f\u6b63\u5220\u9664\uff0c3 \u79d2\u672a\u786e\u8ba4\u81ea\u52a8\u590d\u539f *\/\n  admTbody.addEventListener(\"click\", function (e) {\n    var btn = e.target.closest ? e.target.closest(\"button.adm-del\") : null;\n    if (!btn) return;\n    var idx = parseInt(btn.getAttribute(\"data-idx\"), 10);\n    if (isNaN(idx) || idx < 0 || idx >= CERTS.length) return;\n    if (btn.getAttribute(\"data-armed\") !== \"1\") {\n      btn.setAttribute(\"data-armed\", \"1\");\n      btn.textContent = \"\u786e\u8ba4\u5220\u9664\uff1f\";\n      btn.classList.add(\"danger\");\n      clearTimeout(btn._t);\n      btn._t = setTimeout(function () {\n        btn.removeAttribute(\"data-armed\");\n        btn.textContent = \"\u5220\u9664\";\n        btn.classList.remove(\"danger\");\n      }, 3000);\n      return;\n    }\n    clearTimeout(btn._t);\n    var rec = CERTS[idx];\n    var x = new XMLHttpRequest();\n    x.open(\"POST\", CERTS_API);\n    x.setRequestHeader(\"Content-Type\", \"application\/json\");\n    x.timeout = 10000;\n    x.onreadystatechange = function () {\n      if (x.readyState !== 4) return;\n      var ok = false;\n      try {\n        var d = JSON.parse(x.responseText);\n        ok = d.ok === true;\n        if (d.certs) { CERTS.length = 0; Array.prototype.push.apply(CERTS, d.certs); }\n      } catch (e2) {}\n      if (ok) {\n        addMsg.textContent = \"\u5df2\u5220\u9664 \" + rec.callsign + \" \/ \" + rec.certNo + \"\uff0c\u5df2\u4fdd\u5b58\u5230\u670d\u52a1\u5668\u3002\";\n      } else {\n        addMsg.textContent = \"\u5220\u9664\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5\u3002\";\n      }\n      addMsg.hidden = false;\n      renderTable(); renderExport();\n    };\n    x.onerror = function () { addMsg.textContent = \"\u7f51\u7edc\u9519\u8bef\uff0c\u5220\u9664\u5931\u8d25\u3002\"; addMsg.hidden = false; };\n    x.ontimeout = function () { addMsg.textContent = \"\u670d\u52a1\u5668\u65e0\u54cd\u5e94\uff0c\u5220\u9664\u5931\u8d25\u3002\"; addMsg.hidden = false; };\n    x.send(JSON.stringify({ u: admUser.value.trim().toUpperCase(), p: admPass.value, action: \"delete\", callsign: rec.callsign, certNo: rec.certNo }));\n  });\n\n  function renderExport() {\n    var arr = CERTS.map(function (r) {\n      var o = { callsign: r.callsign, certNo: r.certNo };\n      [\"name\", \"event\", \"date\", \"note\"].forEach(function (k) { if (r[k]) o[k] = r[k]; });\n      if (r.revoked) o.revoked = true;\n      return o;\n    });\n    admExport.value = \"var CERTS = \" + JSON.stringify(arr, null, 2) + \";\";\n  }\n\n  addForm.addEventListener(\"submit\", function (e) {\n    e.preventDefault();\n    addMsg.hidden = true; addErr.hidden = true;\n    var cs = norm($(\"nCallsign\").value), cn = norm($(\"nCertNo\").value);\n    if (!cs || !cn) { addErr.textContent = \"\u547c\u53f7\u548c\u8bc1\u4e66\u7f16\u53f7\u4e3a\u5fc5\u586b\u9879\u3002\"; addErr.hidden = false; return; }\n    var rec = { callsign: cs, certNo: cn };\n    [\"nName\", \"nEvent\", \"nDate\", \"nNote\"].forEach(function (id) {\n      var v = $(id).value.trim();\n      if (v) rec[id.replace(\"n\", \"\").toLowerCase()] = v;\n    });\n    var x = new XMLHttpRequest();\n    x.open(\"POST\", CERTS_API);\n    x.setRequestHeader(\"Content-Type\", \"application\/json\");\n    x.timeout = 10000;\n    x.onreadystatechange = function () {\n      if (x.readyState !== 4) return;\n      var ok = false, err = \"\";\n      try {\n        var d = JSON.parse(x.responseText);\n        ok = d.ok === true;\n        err = d.error || \"\";\n        if (d.certs) { CERTS.length = 0; Array.prototype.push.apply(CERTS, d.certs); }\n      } catch (e2) {}\n      if (ok) {\n        addMsg.textContent = \"\u5df2\u6dfb\u52a0 \" + cs + \" \/ \" + cn + \"\uff0c\u5df2\u4fdd\u5b58\u5230\u670d\u52a1\u5668\uff0c\u6240\u6709\u8bbf\u5ba2\u53ef\u67e5\u3002\";\n        addMsg.hidden = false;\n        addForm.reset();\n      } else if (err === \"exists\") {\n        addErr.textContent = \"\u8be5\u547c\u53f7 + \u8bc1\u4e66\u7f16\u53f7\u5df2\u5b58\u5728\uff0c\u65e0\u9700\u91cd\u590d\u6dfb\u52a0\u3002\";\n        addErr.hidden = false;\n      } else {\n        addErr.textContent = \"\u4fdd\u5b58\u5931\u8d25\uff0c\u8bf7\u91cd\u8bd5\u3002\";\n        addErr.hidden = false;\n      }\n      renderTable(); renderExport();\n    };\n    x.onerror = function () { addErr.textContent = \"\u7f51\u7edc\u9519\u8bef\uff0c\u4fdd\u5b58\u5931\u8d25\u3002\"; addErr.hidden = false; };\n    x.ontimeout = function () { addErr.textContent = \"\u670d\u52a1\u5668\u65e0\u54cd\u5e94\uff0c\u4fdd\u5b58\u5931\u8d25\u3002\"; addErr.hidden = false; };\n    x.send(JSON.stringify({ u: admUser.value.trim().toUpperCase(), p: admPass.value, action: \"add\", cert: rec }));\n  });\n\n  $(\"admCopy\").addEventListener(\"click\", function () {\n    copyMsg.hidden = true;\n    function fallbackCopy() {\n      try {\n        admExport.select();\n        done(document.execCommand(\"copy\"));\n      } catch (e2) { done(false); }\n    }\n    function done(ok) {\n      copyMsg.textContent = ok ? \"\u5df2\u590d\u5236\uff08\u5907\u4efd\u7528\u9014\uff0c\u6570\u636e\u672c\u8eab\u5df2\u4fdd\u5b58\u5728\u670d\u52a1\u5668\uff09\u3002\" : \"\u590d\u5236\u5931\u8d25\uff0c\u8bf7\u624b\u52a8\u5168\u9009\u4e0b\u65b9\u4ee3\u7801\u590d\u5236\u3002\";\n      copyMsg.hidden = false;\n    }\n    if (navigator.clipboard) {\n      if (navigator.clipboard.writeText) {\n        navigator.clipboard.writeText(admExport.value).then(function () { done(true); }, function () { fallbackCopy(); });\n      } else { fallbackCopy(); }\n    } else { fallbackCopy(); }\n  });\n\n  if (location.hash === \"#admin\") showAdmin();\n})();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>AMATEUR RADIO \u00b7 SSTV SSTV \u6d3b\u52a8\u8bc1\u4e66\u9a8c\u771f \u8f93\u5165\u8bc1\u4e66\u4e0a\u7684\u547c\u53f7\u4e0e\u8bc1\u4e66\u7f16\u53f7\uff0c\u67e5\u9a8c\u8be5\u8bc1\u4e66\u662f\u5426\u7531&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","footnotes":""},"class_list":["post-137","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.fmor.cn\/index.php\/wp-json\/wp\/v2\/pages\/137","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.fmor.cn\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.fmor.cn\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.fmor.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fmor.cn\/index.php\/wp-json\/wp\/v2\/comments?post=137"}],"version-history":[{"count":10,"href":"https:\/\/www.fmor.cn\/index.php\/wp-json\/wp\/v2\/pages\/137\/revisions"}],"predecessor-version":[{"id":160,"href":"https:\/\/www.fmor.cn\/index.php\/wp-json\/wp\/v2\/pages\/137\/revisions\/160"}],"wp:attachment":[{"href":"https:\/\/www.fmor.cn\/index.php\/wp-json\/wp\/v2\/media?parent=137"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}