葫芦侠板块签到 · 落地 PHP 源码(v3 UI 优化版)
基于 api.hk0.cc 网关实现葫芦侠(三楼)板块一键签到,单文件即可运行。
本版本增强
- 支持手动选择板块签到(可多选)——板块列表从
/api/hlx?action=categories实时拉取,支持动态板块数量 - 账号密码在页面输入并保存在【本地浏览器 localStorage】,不会写死到源码
- 全新玻璃拟态 UI,适配移动端:板块搜索过滤、密码显示/隐藏、结果分色明细 + 成功/失败/经验汇总卡片
- 单个 / 多个 / 一键全部签到,实时回显每条结果与经验值
运行环境
| PHP | 7.0 及以上,推荐 7.4 / 8.x |
| 扩展 | 需开启 cUrl(宝塔面板默认已开启) |
| 数据库 | 无需 MySQL |
| 框架 | 无需任何框架,单文件即可运行 |
部署步骤
- 将
hlxa_signin.php上传到任意可用 PHP 站点的网站目录 - 浏览器访问该文件
- 输入葫芦侠账号(手机号)与密码(自动保存在本地浏览器,下次免输)
- 勾选要签到的板块,或点 签到全部板块
命令行自动签到(可配宝塔计划任务 / crontab)
php hlxa_signin.php --auto --key=你的key cat_id1 cat_id2 # 签指定板块 php hlxa_signin.php --auto --all # 签全部板块
不传 --key 时,可把 {"account":"手机号","password":"密码"} 写入同目录 .hlxa_auther 自动登录,避免源码写死账号密码。
接口来源(api.hk0.cc 网关)
- 板块列表
https://api.hk0.cc/api/hlx?action=categories - 登录
https://api.hk0.cc/api/hlxa?action=login&account=&password= - 签到
https://api.hk0.cc/api/hlxa?action=signin&_key=&cat_id=
附件下载
完整源码与使用说明见附件:hlxa_signin_v3.zip(内含 hlxa_signin.php + README.txt,共 8986 字节)
v3 相对 v2 的改进
- 全新玻璃拟态 UI,适配移动端
- 板块搜索过滤、密码可见切换
- 签到结果分色明细 + 成功/失败/经验汇总卡片
- 本地记忆账号密码与上次勾选的板块
源码(完整版见附件)
<?php
/**
* =============================================================================
* 葫芦侠(三楼)板块签到 · 落地 PHP 源码(UI 优化版 v3)
* =============================================================================
* 基于 api.hk0.cc 网关接口实现签到:
* - 板块列表 GET /api/hlx?action=categories (全部板块,前端勾选)
* - 登录 GET /api/hlxa?action=login (拿 _key)
* - 签到 GET /api/hlxa?action=signin (单板块 cat_id 或全部)
*
* 账号密码在页面输入并保存在本地浏览器(localStorage),不会写死到源码。
*
* 【运行环境】
* PHP 7.0+,开启 cUrl 扩展(宝塔面板默认已开启)。
* 无需 MySQL、无需任何框架,单文件即可运行。
*
* 【部署】
* 1. 本文件另存为 hlxa_signin.php,上传到任意 PHP 站点目录。
* 2. 浏览器访问该文件即可。
* 3. 命令行/计划任务自动签到时:
* php hlxa_signin.php --auto --key=你的key cat_id1 cat_id2
* php hlxa_signin.php --auto --all
* 未配 --key 时,请将 {"account":"手机号","password":"密码"} 写入
* 本文件同目录的 .hlxa_auther(不写死到源码)。
* =============================================================================
*/
// ---------------- 接口地址(可改) ----------------
$API = array(
'categories' => 'https://api.hk0.cc/api/hlx?action=categories',
'login' => 'https://api.hk0.cc/api/hlxa?action=login',
'signin' => 'https://api.hk0.cc/api/hlxa?action=signin',
);
// ---------------- 工具:转发请求 ----------------
function sk_proxy($url, $timeout = 20) {
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => min(10, $timeout),
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_USERAGENT => 'Mozilla/5.0 (hlxa-signin)',
));
$res = curl_exec($ch);
$err = curl_error($ch);
curl_close($ch);
if ($res === false) {
return array('code' => 502, 'success' => false, 'message' => 'cUrl 错误: ' . $err);
}
$j = json_decode($res, true);
return is_array($j) ? $j : array('code' => 503, 'success' => false, 'message' => $res);
}
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if (($_SERVER['REQUEST_METHOD'] ?? '') === 'OPTIONS') { exit; }
// ---------------- CLI / 定时自动签到 ----------------
if ((PHP_SAPI === 'cli') && array_key_exists('argv', $_SERVER)) {
$args = $_SERVER['argv'];
$mode = 'auto';
$opt = array('key' => '', 'cats' => array(), 'all' => false);
foreach (array_slice($args, 1) as $a) {
if ($a === '--auto') { $mode = 'auto'; }
elseif (strpos($a, '--key=') === 0) { $opt['key'] = substr($a, 6); }
elseif ($a === '--all') { $opt['all'] = true; }
elseif (ctype_digit($a)) { $opt['cats'][] = (int)$a; }
}
if ($mode !== 'auto') { exit("用法: php 本文件.php [cat_id] [--key=xxx] [--all]\n"); }
exit(sk_cli_auto($opt));
}
// ---------------- HTTP 接口(前端调用) ----------------
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'page';
if ($action === 'categories') {
echo json_encode(sk_proxy($API['categories']), JSON_UNESCAPED_UNICODE);
exit;
}
if ($action === 'login') {
$account = isset($_POST['account']) ? trim((string)$_POST['account']) : '';
$password = isset($_POST['password']) ? trim((string)$_POST['password']) : '';
if ($account === '' || $password === '') {
echo json_encode(array('code' => 400, 'success' => false, 'message' => '请输入账号与密码'));
exit;
}
$url = $API['login'] . '&account=' . rawurlencode($account) . '&password=' . rawurlencode($password);
echo json_encode(sk_proxy($url), JSON_UNESCAPED_UNICODE);
exit;
}
if ($action === 'signin') {
$key = isset($_POST['key']) ? trim((string)$_POST['key']) : '';
$all = (isset($_POST['all']) && (string)$_POST['all'] === '1');
$catsRaw = isset($_POST['cat_ids']) ? (array)$_POST['cat_ids'] : array();
if ($key === '') {
echo json_encode(array('code' => 400, 'success' => false, 'message' => '请先登录获取 _key'));
exit;
}
$cats = array();
foreach ($catsRaw as $c) { if (ctype_digit((string)$c) && (int)$c > 0) $cats[] = (int)$c; }
$cats = array_values(array_unique($cats));
$result = array('code' => 200, 'success' => true, 'total' => count($cats), 'results' => array());
if ($all) {
$url = $API['signin'] . '&_key=' . rawurlencode($key);
$r = sk_proxy($url);
if (empty($r['success'])) {
$result['success'] = false;
$result['message'] = isset($r['message']) ? $r['message'] : ('错误 code=' . $r['code']);
} else {
$d = isset($r['data']) ? $r['data'] : array();
$result['total'] = isset($d['total']) ? $d['total'] : 0;
$result['success_count'] = isset($d['success_count']) ? $d['success_count'] : 0;
$result['fail_count'] = isset($d['fail_count']) ? $d['fail_count'] : 0;
$result['total_experience']= isset($d['total_experience']) ? $d['total_experience'] : 0;
if (!empty($d['details'])) $result['results'] = $d['details'];
}
} else {
$ok = 0; $fail = 0; $exp = 0;
foreach ($cats as $cid) {
$url = $API['signin'] . '&_key=' . rawurlencode($key) . '&cat_id=' . $cid;
$r = sk_proxy($url);
$item = array('cat_id' => $cid, 'success' => false, 'experience_val' => 0, 'message' => '');
if (!empty($r['success'])) {
$dd = isset($r['data']) ? $r['data'] : array();
$item['success'] = true;
$item['experience_val'] = isset($dd['experience_val']) ? $dd['experience_val'] : 0;
$item['continue_days'] = isset($dd['continue_days']) ? $dd['continue_days'] : 0;
$item['is_first_sign'] = isset($dd['is_first_sign_today']) ? $dd['is_first_sign_today'] : 0;
$exp += $item['experience_val']; $ok++;
} else {
$item['message'] = isset($r['message']) ? $r['message'] : 'error';
$fail++;
}
$result['results'][] = $item;
}
$result['success_count'] = $ok;
$result['fail_count'] = $fail;
$result['total_experience'] = $exp;
}
echo json_encode($result, JSON_UNESCAPED_UNICODE);
exit;
}
// ---------------- CLI 自动签到实现 ----------------
function sk_cli_auto($opt) {
global $API;
$key = $opt['key'];
$lines = array();
$lines[] = '========== 葫芦侠自动签到 ' . date('Y-m-d H:i:s') . ' ==========';
$log = function ($s) use (&$lines) { $lines[] = $s; };
if ($key === '') {
$confFile = __DIR__ . '/.hlxa_auther';
if (is_file($confFile)) {
$c = @json_decode(trim(file_get_contents($confFile)), true);
if (is_array($c) && !empty($c['account']) && !empty($c['password'])) {
$url = $API['login'] . '&account=' . rawurlencode($c['account']) . '&password=' . rawurlencode($c['password']);
$r = sk_proxy($url);
if (!empty($r['success']) && !empty($r['data']['_key'])) {
$key = $r['data']['_key'];
$log('[登录成功] ' . (isset($r['data']['nick']) ? $r['data']['nick'] : ''));
} else {
$log('[登录失败] ' . (isset($r['message']) ? $r['message'] : ''));
}
}
}
if ($key === '') {
$log('[错误] 未找到 .hlxa_auther 配置文件,或账号密码有误。');
$log('[提示] 首次 CLI 自动签到:将 {"account":"手机号","password":"密码"} 写入本文件同目录 .hlxa_auther');
return implode("\n", $lines) . "\n";
}
}
if ($opt['all'] || empty($opt['cats'])) {
$url = $API['signin'] . '&_key=' . rawurlencode($key);
$r = sk_proxy($url);
if (empty($r['success'])) {
$log('[签到失败] ' . (isset($r['message']) ? $r['message'] : ''));
} else {
$d = isset($r['data']) ? $r['data'] : array();
$log('[全部板块] 共 ' . $d['total'] . ' 板块, 成功 ' . $d['success_count'] . ', 失败 ' . $d['fail_count'] . ', 经验 +' . $d['total_experience']);
if (!empty($d['details'])) {
foreach ($d['details'] as $it) {
$log(sprintf(" %-4s %-12s %s +%d", $it['cat_id'], isset($it['title']) ? $it['title'] : '', $it['success'] ? '成功' : '失败', isset($it['experience_val']) ? $it['experience_val'] : 0));
}
}
}
} else {
foreach ($opt['cats'] as $cid) {
$url = $API['signin'] . '&_key=' . rawurlencode($key) . '&cat_id=' . $cid;
$r = sk_proxy($url);
if (!empty($r['success'])) {
$d = isset($r['data']) ? $r['data'] : array();
$log('[板块 ' . $cid . '] 成功, +' . $d['experience_val'] . ' 经验, 连续 ' . $d['continue_days'] . ' 天');
} else {
$log('[板块 ' . $cid . '] 失败, ' . (isset($r['message']) ? $r['message'] : ''));
}
}
}
$log('========== 完成 ==========');
return implode("\n", $lines) . "\n";
}
// ---------------- 页面(HTML) ----------------
if ($action === 'page') {
header('Content-Type: text/html; charset=utf-8');
echo <<<'HTML'
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#0f2027">
<title>葫芦侠板块签到</title>
<style>
:root{--bg1:#0f2027;--bg2:#203a43;--bg3:#2c5364;--card:rgba(255,255,255,.07);--line:rgba(255,255,255,.12);--txt:#e8eef5;--mut:#93a1b4;--acc:#4da3ff;--acc2:#9b6bff;--ok:#41d17a;--bad:#ff6b6b}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif;background:linear-gradient(135deg,var(--bg1),var(--bg2),var(--bg3));min-height:100vh;color:var(--txt);padding:clamp(12px,3vw,28px)}
.wrap{max-width:760px;margin:0 auto}
.card{background:var(--card);border:1px solid var(--line);border-radius:18px;padding:clamp(16px,4vw,26px);margin-bottom:16px;backdrop-filter:blur(8px);box-shadow:0 12px 40px rgba(0,0,0,.25)}
.head{display:flex;align-items:center;gap:10px;margin-bottom:6px}
.head .logo{width:40px;height:40px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:22px;background:linear-gradient(135deg,var(--acc),var(--acc2))}
h1{font-size:20px;line-height:1.2}
h1 small{display:block;font-size:12px;font-weight:400;color:var(--mut);margin-top:2px}
.sub{color:var(--mut);font-size:13px;margin-bottom:4px}
label{display:block;font-size:13px;color:#b8c4d2;margin:12px 0 6px}
input[type=text],input[type=password]{width:100%;background:rgba(0,0,0,.28);border:1px solid var(--line);border-radius:10px;padding:11px 12px;color:var(--txt);font-size:15px;outline:none;transition:.15s}
input:focus{border-color:var(--acc);box-shadow:0 0 0 3px rgba(77,163,255,.15)}
.pwdwrap{position:relative}
.pwdwrap .eye{position:absolute;right:10px;top:50%;transform:translateY(-50%);background:none;border:none;color:var(--mut);cursor:pointer;font-size:16px;padding:4px}
.toolbar{display:flex;justify-content:space-between;align-items:center;gap:8px;margin:14px 0 8px}
.toolbar label{margin:0}
.search{flex:1;max-width:220px;position:relative}
.search input{width:100%;padding:8px 10px;padding-right:28px;font-size:13px}
.search .sicon{position:absolute;right:10px;top:50%;transform:translateY(-50%);color:var(--mut);font-size:13px;pointer-events:none}
.cats{max-height:340px;overflow-y:auto;border:1px solid var(--line);border-radius:12px;padding:4px}
.cat{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:10px;cursor:pointer;transition:background .12s}
.cat:hover{background:rgba(255,255,255,.06)}
.cat.off{opacity:.45}
.cat img{width:34px;height:34px;border-radius:9px;object-fit:cover;background:rgba(255,255,255,.08);flex:0 0 auto}
.cat .t{flex:1;min-width:0}
.cat .t b{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cat .t small{color:var(--mut);font-size:12px}
.cat input{transform:scale(1.25);accent-color:var(--acc);flex:0 0 auto}
.selbar{display:flex;justify-content:space-between;align-items:center;margin:6px 4px 0}
.selbar .info{color:var(--mut);font-size:12px}
.summary{display:flex;gap:10px;margin-top:14px}
.summary .s{flex:1;text-align:center;background:rgba(0,0,0,.22);border:1px solid var(--line);border-radius:12px;padding:10px 6px}
.summary .s b{display:block;font-size:20px}
.summary .s span{color:var(--mut);font-size:11px}
.summary .s.ok b{color:var(--ok)}
.summary .s.bad b{color:var(--bad)}
.summary .s.exp b{color:var(--acc2)}
.btn{background:linear-gradient(135deg,var(--acc),var(--acc2));border:none;color:#fff;border-radius:12px;padding:13px 20px;font-size:15px;cursor:pointer;width:100%;font-weight:600;transition:opacity .15s,transform .08s;margin-top:14px}
.btn:hover{opacity:.9}
.btn:active{transform:scale(.99)}
.btn:disabled{opacity:.5;cursor:not-allowed}
.btn.ghost{background:rgba(255,255,255,.08);border:1px solid var(--line);width:auto;padding:7px 12px;font-size:13px;font-weight:400}
.btn.ghost:disabled{opacity:.4}
.msg{margin-top:14px;border-radius:12px;padding:14px;font-size:13px;line-height:1.7;background:rgba(0,0,0,.3);display:none;max-height:320px;overflow-y:auto}
.msg.open{display:block}
.msg.ok{border:1px solid rgba(65,209,122,.35)}
.msg.err{border:1px solid rgba(255,107,107,.4)}
.msg .line{display:flex;justify-content:space-between;gap:8px;padding:4px 2px;border-bottom:1px dashed rgba(255,255,255,.07)}
.msg .line:last-child{border-bottom:none}
.msg .line .st{flex:0 0 auto}
.msg .line.ok .st{color:var(--ok)}
.msg .line.bad .st{color:var(--bad)}
.msg .blank{color:var(--mut);text-align:center;padding:12px 0}
.foot{color:#5b6b79;font-size:12px;text-align:center;margin-top:2px}
@media(max-width:520px){.summary{gap:6px}.summary .s b{font-size:17px}.search{max-width:150px}}
</style>
</head>
<body>
<div class="wrap">
<div class="card">
<div class="head">
<div class="logo">🌿</div>
<h1>葫芦侠板块签到<small>落地版 v3 · PHP 7.0+ · 单文件</small></h1>
</div>
<div class="sub">账号密码仅保存在当前浏览器(localStorage),不会写入服务器文件。</div>
<label>葫芦侠账号(手机号)</label>
<input type="text" id="account" placeholder="请输入葫芦侠账号" autocomplete="off">
<label>密码</label>
<div class="pwdwrap">
<input type="password" id="password" placeholder="请输入密码">
<button class="eye" id="eye" type="button" title="显示/隐藏密码">👁</button>
</div>
<div class="toolbar">
<label>选择签到板块(可多选)</label>
<div class="search">
<input type="text" id="kw" placeholder="搜索板块…">
<span class="sicon">⌕</span>
</div>
</div>
<div class="cats" id="cats"><div class="sub" style="padding:12px">正在加载板块…</div></div>
<div class="selbar">
<span class="info" id="selinfo">已选 0 / 0 个板块</span>
<button class="btn ghost" id="selAll">全选</button>
</div>
<button class="btn" id="signBtn">一键签到选中板块</button>
<button class="btn ghost" id="signAllBtn" style="width:100%">签到全部板块</button>
<div class="summary" id="summary" style="display:none">
<div class="s ok"><b id="sOk">0</b><span>签到成功</span></div>
<div class="s bad"><b id="sBad">0</b><span>失败</span></div>
<div class="s exp"><b id="sExp">+0</b><span>经验</span></div>
</div>
<div class="msg" id="msg"></div>
</div>
<div class="foot">接口由 api.hk0.cc 提供 · GUI 模式浏览器访问 · CLI 模式支持宝塔计划任务 / crontab</div>
</div>
<script>
(function(){
var $=function(id){return document.getElementById(id)};
var KA="sk_hlxa_account", KP="sk_hlxa_pass", KS="sk_hlxa_cats";
var cats=[], sel={}, kw="";
if(localStorage.getItem(KA)) $("account").value=localStorage.getItem(KA);
if(localStorage.getItem(KP)) $("password").value=localStorage.getItem(KP);
try{ sel=JSON.parse(localStorage.getItem(KS)||"{}"); }catch(e){ sel={}; }
function save(){
localStorage.setItem(KA,$("account").value);
localStorage.setItem(KP,$("password").value);
localStorage.setItem(KS,JSON.stringify(sel));
}
function esc(s){ var d=document.createElement("div"); d.textContent=s==null?"":String(s); return d.innerHTML; }
$("eye").onclick=function(){
var p=$("password");
var show=p.type==="password";
p.type=show?"text":"password";
this.textContent=show?"🙈":"👁";
};
$("kw").addEventListener("input", function(){ kw=this.value.trim().toLowerCase(); renderCats(); });
function filtered(){
if(!kw) return cats;
return cats.filter(function(c){ return (c.title||"").toLowerCase().indexOf(kw)>=0; });
}
function renderCats(){
var box=$("cats"), list=filtered();
if(!cats.length){ box.innerHTML='<div class="sub" style="padding:12px">暂无板块数据</div>'; return; }
var h="";
list.forEach(function(c){
var on=!!sel[c.id];
h+='<div class="cat"'+(kw ? ' data-k="'+esc(c.title)+'"' : '')+' onclick="toggle('+c.id+')">'
+'<input type="checkbox"'+(on ? ' checked' : '')+' onclick="event.stopPropagation();toggle('+c.id+')">'
+'<img src="'+esc(c.icon)+'" onerror="this.style.display=\'none\'">'
+'<div class="t"><b>'+esc(c.title)+'</b><small>帖子 '+(c.post_size>=0?c.post_size:(c.post_count||0))+'</small></div>'
+'</div>';
});
box.innerHTML=h||'<div class="sub" style="padding:12px">未找到匹配板块</div>';
updSel();
}
window.toggle=function(id){
sel[id]=sel[id]?undefined:true;
save(); renderCats();
};
function countSel(){ var n=0; for(var k in sel) if(sel[k]) n++; return n; }
function updSel(){
var n=countSel();
$("selinfo").textContent="已选 "+n+" / "+cats.length+" 个板块";
$("selAll").textContent=(n&&n===cats.length)?"取消全选":"全选";
}
$("selAll").onclick=function(){
if(countSel()===cats.length){ sel={}; }
else { cats.forEach(function(c){ sel[c.id]=true; }); }
save(); renderCats();
};
function show(msg, ok){
var m=$("msg");
m.className="msg open "+(ok?"ok":"err");
m.textContent=msg;
}
function showLines(lines, ok){
var m=$("msg");
m.className="msg open "+(ok?"ok":"err");
if(!lines.length){ m.innerHTML='<div class="blank">暂无明细</div>'; return; }
var h="";
lines.forEach(function(it){
if(it.kind==="blank"){ h+='<div class="blank">'+esc(it.text)+'</div>'; return; }
h+='<div class="line '+(it.ok?'ok':'bad')+'"><span>'+esc(it.text)+'</span><span class="st">'+esc(it.st)+'</span></div>';
});
m.innerHTML=h;
}
function setSummary(ok, bad, exp){
var s=$("summary");
s.style.display=ok||bad?"flex":"none";
$("sOk").textContent=ok; $("sBad").textContent=bad; $("sExp").textContent=exp>=0?("+"+exp):"-";
}
function setBusy(btn, busy, label){
btn.disabled=busy;
btn.textContent=busy?("签到中…"):label;
}
function api(action, data, cb, errCb){
var fd=new FormData();
for(var k in data) fd.append(k,data[k]);
fetch("?action="+action,{method:"POST",body:fd})
.then(function(r){return r.json()})
.then(cb)
.catch(function(e){ if(errCb) errCb(e); });
}
function buildLines(res, items){
var lines=[];
if(items && items.length){
items.forEach(function(r){
var d=r;
var ok1=!!r.success;
var exp1=r.experience_val||0;
lines.push({ok:ok1, text:"板块 "+r.cat_id+" · "+(r.title||""), st:ok1?("成功 +"+exp1+" 经验"):("失败 "+(r.message||"未知"))});
});
}
if(res.total_experience!=null){
lines.unshift({ok:true, text:"签到完成", st:"共"+res.total+" / 成功"+res.success_count+" / 失败"+res.fail_count});
}
setSummary(res.success_count||0, res.fail_count||0, res.total_experience);
return lines;
}
function sign(kind, _key){
var account=$("account").value.trim(), password=$("password").value;
if(!account||!password){ show("请先输入账号和密码",false); return; }
save();
var btn=(kind==="all")?$("signAllBtn"):$("signBtn");
setBusy(btn,true,btn.textContent);
show("正在登录…",true);
api("login",{account:account,password:password},function(login){
if(!login.success){
setBusy(btn,false,kind==="all"?"签到全部板块":"一键签到选中板块");
show("登录失败:"+(login.message||"未知错误"),false);
return;
}
var key=login.data._key;
var data={key:key};
if(kind==="all"){ data.all="1"; }
else{
var ids=[]; for(var k in sel) if(sel[k]) ids.push(k);
if(!ids.length){
setBusy(btn,false,"一键签到选中板块");
show("未选择任何板块",false); return;
}
data.cat_ids=ids;
}
setBusy(btn,true,"签到中…");
show("正在签到…",true);
api("signin",data,function(res){
setBusy(btn,false,kind==="all"?"签到全部板块":"一键签到选中板块");
if(res.success){
var lines=buildLines(res, res.results||[]);
showLines(lines, true);
} else {
setSummary(0,0,-1);
show(res.message||"签到失败",false);
}
},function(e){ setBusy(btn,false,kind==="all"?"签到全部板块":"一键签到选中板块"); show("网络错误:"+e,false); });
},function(e){ setBusy(btn,false,kind==="all"?"签到全部板块":"一键签到选中板块"); show("网络错误:"+e,false); });
}
$("signBtn").onclick=function(){ sign("sel"); };
$("signAllBtn").onclick=function(){ sign("all"); };
fetch("?action=categories").then(function(r){return r.json()}).then(function(d){
if(d.success){
cats=d.data||[];
for(var i=0;i<cats.length;i++){ var c=cats[i]; if(c.post_size==null&&c.post_count!=null) c.post_size=c.post_count; }
// 默认勾选上次选择;若从未选过则全不选
} else {
$("cats").innerHTML='<div class="sub" style="padding:12px">加载板块失败:'+esc(d.message||d.code)+'</div>';
}
renderCats();
}).catch(function(){
$("cats").innerHTML='<div class="sub" style="padding:12px">加载板块失败(网络错误)</div>';
renderCats();
});
})();
</script>
</body>
</html>
HTML;
}
完整 500 行源码(含全新 UI 与 CLI 自动签到)见附件 hlxa_signin_v3.zip。
hlxa_signin_v3.zip
8.8KB