Nine7 / listapp.html
ljx77qaq's picture
Upload 3 files
ee6c12e verified
Raw
History Blame Contribute Delete
7.57 kB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover, interactive-widget=overlays-content">
<title>完整名单</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@700;800&family=Space+Grotesk:wght@400;500;600;700&family=Noto+Sans+SC:wght@400;500;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root{--bg:#070709;--panel:rgba(255,255,255,.03);--ink:#EFEDE6;--mut:rgba(239,237,230,.48);--dim:rgba(239,237,230,.26);
--acc:#D9FF3D;--vio:#9B8CFF;--line:rgba(239,237,230,.09);--line2:rgba(239,237,230,.18);
--fd:"Syne",sans-serif;--fb:"Space Grotesk","Noto Sans SC",sans-serif;--fm:"JetBrains Mono",monospace}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
body{background:var(--bg);color:var(--ink);font:400 14px/1.6 var(--fb);min-height:100dvh;padding:18px 14px 30px;overflow-x:hidden}
.glow{position:fixed;width:70vw;height:70vw;max-width:520px;max-height:520px;border-radius:50%;filter:blur(120px);
background:var(--acc);opacity:.1;top:-26vw;right:-22vw;pointer-events:none;z-index:0}
.wrap{position:relative;z-index:1;max-width:620px;margin:0 auto}
h1{font:800 clamp(21px,6vw,30px)/1.15 var(--fd);letter-spacing:-.01em;text-align:center}
.sub{text-align:center;font:500 10.5px var(--fm);color:var(--mut);letter-spacing:.1em;margin-top:8px}
.sub b{color:var(--acc);font-weight:700}
.srch{position:relative;margin:18px 0 12px}
.srch input{width:100%;background:rgba(255,255,255,.04);border:1px solid var(--line);color:var(--ink);
padding:11px 14px 11px 38px;border-radius:12px;font:500 16px var(--fb);outline:none;transition:.25s}
.srch input:focus{border-color:var(--acc);background:rgba(217,255,61,.05)}
.srch svg{position:absolute;left:13px;top:50%;transform:translateY(-50%);width:15px;height:15px;stroke:var(--dim);fill:none;stroke-width:2}
.i{display:flex;align-items:center;gap:11px;padding:12px 14px;border:1px solid var(--line);border-radius:14px;
background:var(--panel);margin-bottom:7px;color:inherit;text-decoration:none;transition:.22s}
.i:active{transform:scale(.985);border-color:var(--acc)}
.i.top .r{color:var(--acc)}
.i .r{font:700 11.5px var(--fm);color:var(--vio);width:34px;flex:none}
.i .n{flex:1;font:500 13.5px var(--fb);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.i .n.done{text-decoration:line-through;opacity:.42}
.i .c{font:700 11.5px var(--fm);color:var(--acc);flex:none}
.i .go{width:13px;height:13px;flex:none;stroke:var(--dim);fill:none;stroke-width:2}
.msg{border:1px dashed var(--line2);border-radius:16px;padding:44px 22px;text-align:center;color:var(--mut);margin-top:24px}
.msg b{display:block;font:700 15px var(--fd);color:var(--ink);margin-bottom:6px}
.foot{margin-top:22px;text-align:center;font:500 9px var(--fm);letter-spacing:.22em;color:var(--dim);text-transform:uppercase}
.sk{height:47px;border-radius:14px;background:linear-gradient(90deg,rgba(255,255,255,.03),rgba(255,255,255,.07),rgba(255,255,255,.03));
background-size:200% 100%;animation:sh 1.2s linear infinite;margin-bottom:7px}
@keyframes sh{to{background-position:-200% 0}}
</style>
</head>
<body>
<div class="glow"></div>
<div class="wrap" id="app">
<h1>完整名单</h1><div class="sub">加载中…</div>
<div style="margin-top:20px"><div class="sk"></div><div class="sk"></div><div class="sk"></div><div class="sk"></div><div class="sk"></div></div>
</div>
<script>
/* 榜单专用 Mini App —— 整页只做一件事:展示完整名单并跳转评论区。
不含控制台任何功能,也不调用任何需要鉴权的接口。 */
const TG=window.Telegram&&window.Telegram.WebApp;
const Q=new URLSearchParams(location.search);
const RAW=(TG&&TG.initDataUnsafe&&TG.initDataUnsafe.start_param)||Q.get('startapp')||Q.get('list')||Q.get('tgWebAppStartParam')||'';
const KEY=String(RAW).replace(/^list[-_]/,'');
const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const SEARCH_SVG='<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg>';
const GO_SVG='<svg class="go" viewBox="0 0 24 24"><path d="M9 18l6-6-6-6"/></svg>';
let ITEMS=[],TITLE='完整名单',UPDATED='',STALE=false;
function open_(href){
if(!href||href==='#'){
if(TG&&TG.showAlert)TG.showAlert('这条评论没有可用的跳转链接,等下一轮榜单刷新');
else alert('这条评论没有可用的跳转链接');
return;
}
try{
if(TG&&TG.openTelegramLink&&/^https:\/\/t\.me\//i.test(href))TG.openTelegramLink(href);
else if(TG&&TG.openLink&&/^https?:/i.test(href))TG.openLink(href);
else{const w2=window.open(href,'_blank');if(!w2)location.href=href}
}catch(e){}
}
function rows(list){
if(!list.length)return `<div class="msg"><b>没有匹配的名字</b>换个关键词试试</div>`;
return list.map(it=>`<a class="i${it.r<=3?' top':''}" href="${esc(it.l||'#')}" data-l="${esc(it.l||'#')}">
<span class="r">#${esc(it.r)}</span>
<span class="n${it.d?' done':''}">${esc(it.n)}</span>
<span class="c">${esc(it.c)} 赞</span>${GO_SVG}</a>`).join('');
}
function paint(kw=''){
const k=kw.trim().toLowerCase();
const list=k?ITEMS.filter(it=>String(it.n||'').toLowerCase().includes(k)):ITEMS;
document.getElementById('list').innerHTML=rows(list);
document.getElementById('cnt').textContent=list.length;
bind();
}
function bind(){
document.querySelectorAll('.i').forEach(a=>a.onclick=e=>{e.preventDefault();open_(a.dataset.l)});
}
function render(){
document.getElementById('app').innerHTML=`
<h1>${esc(TITLE)}</h1>
<div class="sub">更新于 <b>${esc(UPDATED||'—')}</b> · 共 <b id="cnt">${ITEMS.length}</b> 条 · 点击跳转评论区</div>
${STALE?`<div class="msg" style="margin:0 0 12px">这份名单是旧版缓存,评论链接要等下一轮榜单刷新后才补齐。</div>`:''}
<label class="srch">${SEARCH_SVG}<input id="q" type="search" placeholder="搜索名字…" autocomplete="off"></label>
<div id="list"></div>
<div class="foot">NINE7 · FULL LIST</div>`;
paint();
document.getElementById('q').addEventListener('input',e=>paint(e.target.value));
}
function fail(t,s){
document.getElementById('app').innerHTML=`<h1>完整名单</h1>
<div class="msg"><b>${esc(t)}</b>${esc(s)}</div><div class="foot">NINE7 · FULL LIST</div>`;
}
(async()=>{
if(TG){try{TG.ready();TG.expand();TG.setHeaderColor&&TG.setHeaderColor('#070709');TG.setBackgroundColor&&TG.setBackgroundColor('#070709')}catch(e){}}
if(Q.get('demo')){ // 本地预览用
TITLE='本周热评 TOP10(演示)';UPDATED='今天 12:00';
ITEMS=Array.from({length:26},(_,i)=>({r:i+1,n:['夜航星','雾都夜谈','白夜行','长安十二时辰','银河edge'][i%5]+'('+(i+1)+')',
c:180-i*6,l:'https://t.me/c/1682349051/1024?comment='+(100+i),d:i%7===0}));
return render();
}
if(!KEY)return fail('缺少名单参数','请从频道榜单里的「完整名单」链接进入。');
try{
const r=await fetch('/api/list/'+encodeURIComponent(KEY));
const j=await r.json();
if(!r.ok||j.ok===false)throw new Error(j&&j.msg||'名单不存在');
ITEMS=j.items||[];TITLE=j.title||'完整名单';UPDATED=j.updated||'';STALE=!!j.stale;
render();
}catch(e){fail('名单不可用',e.message||'请等榜单下次刷新后重试')}
})();
</script>
</body>
</html>