【soul-admin 管理后台】 - 交易中心 → 推广中心(侧边栏与页面标题) - 移除 5 个冗余按钮,仅保留「API 接口」 - 删除按钮改为悬停显示 - 免费/付费可点击切换(单击切换,双击付费可设金额) - 加号移至章节右侧(序言、附录等),小节内移除加号 - 章节与小节支持拖拽排序 - 持续隐藏「上传内容」等按钮,解决双页面问题 【小程序首页 - 最新章节】 - latest-chapters API: 2 日内有新章取最新 3 章,否则随机免费章 - 首页 Banner 调用 /api/book/latest-chapters - 标签动态显示「最新更新」或「为你推荐」 【开发文档】 - 新增 soul-admin变更记录_v2026-02.md Co-authored-by: Cursor <cursoragent@cursor.com>
438 lines
25 KiB
HTML
438 lines
25 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>管理后台 - Soul创业派对</title>
|
||
<script type="module" crossorigin src="/assets/index-CbOmKBRd.js?v=5"></script>
|
||
<link rel="stylesheet" crossorigin href="/assets/index-DBQ1UORI.css">
|
||
</head>
|
||
<body>
|
||
<div id="root"></div>
|
||
<script>
|
||
(function(){
|
||
var CSS=document.createElement('style');
|
||
CSS.textContent=`
|
||
.si-row-actions{display:inline-flex;align-items:center;gap:4px}
|
||
.si-row-actions .si-del{opacity:0;visibility:hidden;transition:opacity .2s,visibility .2s}
|
||
.si-row-actions:hover .si-del{opacity:1;visibility:visible}
|
||
.si-del{padding:2px 8px;font-size:11px;border-radius:4px;cursor:pointer;background:transparent;
|
||
border:1px solid #7f1d1d;color:#ef4444;margin-left:6px;transition:all .15s}
|
||
.si-del:hover{background:#7f1d1d;color:#fff}
|
||
.si-plus{padding:2px 6px;font-size:12px;border-radius:4px;cursor:pointer;background:transparent;
|
||
border:1px solid #2dd4a8;color:#2dd4a8;margin-left:4px;transition:all .15s}
|
||
.si-plus:hover{background:#2dd4a8;color:#0a0e17}
|
||
.si-free-toggle{padding:2px 8px;font-size:11px;border-radius:4px;cursor:pointer;margin-left:6px;
|
||
border:1px solid #475569;color:#94a3b8;transition:all .15s;user-select:none}
|
||
.si-free-toggle:hover{border-color:#2dd4a8;color:#2dd4a8}
|
||
.si-free-toggle.paid{border-color:#f59e0b;color:#f59e0b}
|
||
.si-drag-handle{cursor:grab;opacity:.5;padding:2px 6px;margin-right:4px;user-select:none}
|
||
.si-drag-handle:active{cursor:grabbing}
|
||
.si-dragging{opacity:.5;background:rgba(45,212,168,.1)}
|
||
.si-drop-target{border:2px dashed #2dd4a8;border-radius:4px}
|
||
.si-panel{background:#111827;border:1px solid #1e293b;border-radius:10px;padding:20px;margin:16px 0}
|
||
.si-panel h3{font-size:15px;margin:0 0 14px;color:#e0e6ed}
|
||
.si-panel label{display:block;font-size:12px;color:#94a3b8;margin:10px 0 4px}
|
||
.si-panel input,.si-panel select,.si-panel textarea{width:100%;padding:8px 10px;box-sizing:border-box;
|
||
background:#0a0e17;border:1px solid #1e293b;border-radius:6px;color:#e0e6ed;font-size:13px;outline:none}
|
||
.si-panel input:focus,.si-panel textarea:focus{border-color:#2dd4a8}
|
||
.si-panel textarea{min-height:160px;font-family:monospace;resize:vertical}
|
||
.si-row{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
||
.si-submit{width:100%;padding:10px;margin-top:14px;background:#2dd4a8;color:#0a0e17;
|
||
border:none;border-radius:6px;font-size:14px;font-weight:600;cursor:pointer}
|
||
.si-submit:hover{background:#22b896}
|
||
.si-api{font-family:monospace;font-size:12px;line-height:1.7;color:#94a3b8}
|
||
.si-api pre{background:#0a0e17;border:1px solid #1e293b;border-radius:6px;padding:12px;
|
||
overflow-x:auto;margin:6px 0 14px;font-size:12px;color:#2dd4a8;white-space:pre-wrap}
|
||
.si-api h4{color:#e0e6ed;font-size:13px;margin:16px 0 4px;font-family:sans-serif}
|
||
.si-token-box{background:#0a0e17;border:1px solid #2dd4a8;border-radius:8px;padding:14px;margin-bottom:20px}
|
||
.si-token-box .si-token-row{display:flex;gap:8px;align-items:center;margin-top:8px}
|
||
.si-token-box input{flex:1;padding:8px 10px;background:#111827;border:1px solid #1e293b;border-radius:6px;color:#2dd4a8;font-size:12px;font-family:monospace}
|
||
.si-token-btn{padding:8px 16px;border-radius:6px;font-size:13px;cursor:pointer;border:none;background:#2dd4a8;color:#0a0e17;font-weight:600}
|
||
.si-token-btn:hover{background:#22b896}
|
||
.si-token-btn.copy{background:#1e293b;color:#e0e6ed}
|
||
.si-token-btn.copy:hover{background:#334155}
|
||
.si-toast{position:fixed;top:16px;right:16px;padding:10px 18px;border-radius:6px;
|
||
font-size:13px;z-index:99999;animation:siFade .25s}
|
||
.si-toast.ok{background:#065f46;color:#6ee7b7}
|
||
.si-toast.err{background:#7f1d1d;color:#fca5a5}
|
||
@keyframes siFade{from{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}
|
||
`;
|
||
document.head.appendChild(CSS);
|
||
|
||
var API=(window.location.hostname||'').indexOf('souladmin')>=0?'':'https://souldev.quwanzhi.com';
|
||
var token=localStorage.getItem('admin_token')||'';
|
||
|
||
function toast(m,ok){var t=document.createElement('div');t.className='si-toast '+(ok!==false?'ok':'err');
|
||
t.textContent=m;document.body.appendChild(t);setTimeout(function(){t.remove()},3000)}
|
||
function apicall(method,path,body){
|
||
var opts={method:method,headers:{'Content-Type':'application/json'}};
|
||
if(token)opts.headers['Authorization']='Bearer '+token;
|
||
if(body)opts.body=JSON.stringify(body);
|
||
return fetch(API+path,opts).then(function(r){return r.json()}).catch(function(e){return{success:false,error:e.message}})
|
||
}
|
||
function auth(){
|
||
if(token)return apicall('GET','/api/admin').then(function(r){if(r.success)return true;return doLogin()});
|
||
return doLogin()
|
||
}
|
||
function doLogin(){
|
||
return apicall('POST','/api/admin',{username:'admin',password:'admin123'}).then(function(r){
|
||
if(r.success&&r.token){token=r.token;localStorage.setItem('admin_token',token);return true}
|
||
return false
|
||
})
|
||
}
|
||
|
||
function findBtn(text){
|
||
var all=document.querySelectorAll('button');
|
||
for(var i=0;i<all.length;i++){if(all[i].textContent.trim()===text)return all[i]}
|
||
return null
|
||
}
|
||
|
||
var done=false;
|
||
|
||
function hideRedundantButtons(){
|
||
['初始化数据库','同步到数据库','导入','导出','同步飞书','上传内容'].forEach(function(t){
|
||
var b=findBtn(t);if(b)b.style.display='none';
|
||
});
|
||
}
|
||
|
||
function run(){
|
||
if(done)return;
|
||
if(!location.pathname.includes('content')&&!location.hash.includes('content'))return;
|
||
var initBtn=findBtn('初始化数据库');
|
||
if(!initBtn)return;
|
||
done=true;
|
||
|
||
// === 1. 移除5个按钮+上传内容,只保留一个"API 接口"(持续执行防重复页)===
|
||
hideRedundantButtons();
|
||
setInterval(hideRedundantButtons,800);
|
||
|
||
var btnParent=initBtn&&initBtn.parentElement;
|
||
if(btnParent&&!btnParent.querySelector('.si-api-only-btn')){
|
||
var apiBtn=document.createElement('button');
|
||
apiBtn.className='si-api-only-btn '+initBtn.className;apiBtn.style.display='inline-flex';
|
||
apiBtn.textContent='API 接口';
|
||
apiBtn.onclick=function(e){e.preventDefault();e.stopPropagation();togglePanel('api')};
|
||
btnParent.appendChild(apiBtn);
|
||
}
|
||
|
||
// === 2. 创建面板(插入到 tabs 之前) ===
|
||
var tabBar=document.querySelector('[role="tablist"]');
|
||
if(!tabBar){
|
||
var tabs=findBtn('章节管理');
|
||
if(tabs)tabBar=tabs.parentElement;
|
||
}
|
||
var insertTarget=tabBar||(initBtn&&initBtn.parentElement);
|
||
|
||
// 上传面板
|
||
var upP=document.createElement('div');
|
||
upP.className='si-panel';upP.id='si-upload';upP.style.display='none';
|
||
upP.innerHTML='<h3>上传新章节</h3>'
|
||
+'<div class="si-row"><div><label>章节ID (留空自动)</label><input id="si-uid" placeholder="如 1.6"></div>'
|
||
+'<div><label>定价 (0=免费)</label><input type="number" id="si-uprice" value="1" step="0.1" min="0"></div></div>'
|
||
+'<label>标题 *</label><input id="si-utitle" placeholder="章节标题">'
|
||
+'<div class="si-row"><div><label>所属篇</label><select id="si-upart">'
|
||
+'<option value="part-1">第一篇|真实的人</option><option value="part-2">第二篇|真实的行业</option>'
|
||
+'<option value="part-3">第三篇|真实的错误</option><option value="part-4">第四篇|真实的赚钱</option>'
|
||
+'<option value="part-5">第五篇|真实的社会</option><option value="appendix">附录</option>'
|
||
+'<option value="intro">序言</option><option value="outro">尾声</option></select></div>'
|
||
+'<div><label>所属章</label><select id="si-uchap">'
|
||
+'<option value="chapter-1">第1章</option><option value="chapter-2">第2章</option>'
|
||
+'<option value="chapter-3">第3章</option><option value="chapter-4">第4章</option>'
|
||
+'<option value="chapter-5">第5章</option><option value="chapter-6">第6章</option>'
|
||
+'<option value="chapter-7">第7章</option><option value="chapter-8">第8章</option>'
|
||
+'<option value="chapter-9">第9章</option><option value="chapter-10">第10章</option>'
|
||
+'<option value="chapter-11">第11章</option><option value="appendix">附录</option>'
|
||
+'<option value="preface">序言</option><option value="epilogue">尾声</option></select></div></div>'
|
||
+'<label>内容 (Markdown) *</label><textarea id="si-ucontent" placeholder="正文内容... 图片占位用 {{image_1}}"></textarea>'
|
||
+'<label>图片URL (每行一个)</label><textarea id="si-uimgs" style="min-height:60px" placeholder="https://example.com/1.png"></textarea>'
|
||
+'<button class="si-submit" id="si-submit-btn">上传章节</button>';
|
||
insertTarget.parentElement.insertBefore(upP,insertTarget);
|
||
|
||
document.getElementById('si-submit-btn').onclick=function(){siUpload()};
|
||
|
||
// API文档面板
|
||
var apiP=document.createElement('div');
|
||
apiP.className='si-panel';apiP.id='si-apidoc';apiP.style.display='none';
|
||
apiP.innerHTML='<div class="si-api">'
|
||
+'<h3 style="font-family:sans-serif">内容管理 API 接口文档</h3>'
|
||
+'<div class="si-token-box"><strong style="color:#e0e6ed">生成 TOKEN</strong> — 用于上传新章节、删除等操作<br>'
|
||
+'<div class="si-token-row"><button class="si-token-btn" id="si-gen-token">生成 TOKEN</button>'
|
||
+'<input type="text" id="si-token-input" readonly placeholder="点击生成后显示,可复制用于 curl/Skill 上传" style="cursor:pointer">'
|
||
+'<button class="si-token-btn copy" id="si-copy-token">复制</button></div></div>'
|
||
+'<p>基础域名: <code>https://soulapi.quwanzhi.com</code> (正式) / <code>https://souldev.quwanzhi.com</code> (开发)</p>'
|
||
+'<h4>1. 获取所有章节 (无需认证)</h4><pre>GET /api/book/all-chapters\n\ncurl https://soulapi.quwanzhi.com/api/book/all-chapters</pre>'
|
||
+'<h4>2. 获取单章内容</h4><pre>GET /api/book/chapter/:id\n\ncurl https://soulapi.quwanzhi.com/api/book/chapter/1.1</pre>'
|
||
+'<h4>3. 管理员登录 (获取Token)</h4><pre>POST /api/admin\nBody: {"username":"admin","password":"admin123"}\n\ncurl -X POST https://souldev.quwanzhi.com/api/admin \\\n -H "Content-Type: application/json" \\\n -d \'{"username":"admin","password":"admin123"}\'</pre>'
|
||
+'<h4>4. 创建/更新章节 (需Token)</h4><pre>POST /api/db/book\nAuthorization: Bearer {token}\nBody: {\n "id": "1.6",\n "title": "标题",\n "content": "Markdown正文",\n "price": 1.0,\n "partId": "part-1",\n "chapterId": "chapter-1"\n}\n\ncurl -X POST https://souldev.quwanzhi.com/api/db/book \\\n -H "Authorization: Bearer TOKEN" \\\n -H "Content-Type: application/json" \\\n -d \'{"id":"1.6","title":"新章节","content":"正文","price":1.0,"partId":"part-1","chapterId":"chapter-1"}\'</pre>'
|
||
+'<h4>5. 删除章节 (需Token)</h4><pre>DELETE /api/admin/content/:id\n\ncurl -X DELETE https://souldev.quwanzhi.com/api/admin/content/1.6 \\\n -H "Authorization: Bearer TOKEN"</pre>'
|
||
+'<h4>6. 命令行上传 (数据库直写)</h4><pre>python3 content_upload.py --title "标题" --price 1.0 --content "正文" \\\n --part part-1 --chapter chapter-1\n\npython3 content_upload.py --list-structure # 查看篇章结构\npython3 content_upload.py --list-chapters # 列出所有章节</pre>'
|
||
+'<h4>7. 数据库直连</h4><pre>Host: 56b4c23f6853c.gz.cdb.myqcloud.com:14413\nUser: cdb_outerroot\nDB: soul_miniprogram\n表: chapters</pre>'
|
||
+'</div>';
|
||
insertTarget.parentElement.insertBefore(apiP,insertTarget);
|
||
|
||
document.getElementById('si-gen-token').onclick=function(){
|
||
var inp=document.getElementById('si-token-input');
|
||
inp.value='获取中...';
|
||
doLogin().then(function(ok){
|
||
if(ok&&token){inp.value=token;toast('TOKEN 已生成,可复制使用')}
|
||
else{inp.value='';toast('获取失败',false)}
|
||
});
|
||
};
|
||
document.getElementById('si-copy-token').onclick=function(){
|
||
var inp=document.getElementById('si-token-input');
|
||
if(!inp.value||inp.value==='获取中...'){toast('请先生成 TOKEN',false);return}
|
||
inp.select();document.execCommand('copy');
|
||
toast('已复制到剪贴板');
|
||
};
|
||
document.getElementById('si-token-input').onclick=function(){this.select()};
|
||
|
||
// === 3. 内容操作:删除(hover)、免费/付费、加号在章节、拖拽 ===
|
||
addContentActions();
|
||
addChapterPlus();
|
||
addDragDrop();
|
||
new MutationObserver(function(){addContentActions();addChapterPlus();addDragDrop();}).observe(document.getElementById('root'),{childList:true,subtree:true});
|
||
}
|
||
|
||
var activePanel='';
|
||
var siPrefill={};
|
||
function togglePanel(name,prefill){
|
||
var up=document.getElementById('si-upload');
|
||
var ap=document.getElementById('si-apidoc');
|
||
if(!up||!ap)return;
|
||
if(prefill)siPrefill=prefill;
|
||
if(activePanel===name&&name!=='upload'){ap.style.display='none';activePanel='';return}
|
||
if(name==='upload'){up.style.display='block';ap.style.display='none';applyPrefill();activePanel='upload';return}
|
||
if(name==='api'){up.style.display='none';ap.style.display='block';activePanel='api';return}
|
||
}
|
||
function applyPrefill(){
|
||
if(siPrefill.partId){var s=document.getElementById('si-upart');if(s)s.value=siPrefill.partId}
|
||
if(siPrefill.chapterId){var c=document.getElementById('si-uchap');if(c)c.value=siPrefill.chapterId}
|
||
}
|
||
function getSectionInfo(row){
|
||
var p=row;
|
||
for(var i=0;i<8&&p;i++){p=p.parentElement;if(!p)break;
|
||
var t=(p.textContent||'').substring(0,80);
|
||
if(/附录/.test(t))return{partId:'appendix',chapterId:'appendix'};
|
||
if(/序言/.test(t))return{partId:'intro',chapterId:'preface'};
|
||
if(/尾声/.test(t))return{partId:'outro',chapterId:'epilogue'};
|
||
if(/第一篇/.test(t))return{partId:'part-1',chapterId:'chapter-1'};
|
||
if(/第二篇/.test(t))return{partId:'part-2',chapterId:'chapter-3'};
|
||
if(/第三篇/.test(t))return{partId:'part-3',chapterId:'chapter-6'};
|
||
if(/第四篇/.test(t))return{partId:'part-4',chapterId:'chapter-8'};
|
||
if(/第五篇/.test(t))return{partId:'part-5',chapterId:'chapter-10'};
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function addContentActions(){
|
||
var all=document.querySelectorAll('button');
|
||
for(var i=0;i<all.length;i++){
|
||
var b=all[i];
|
||
if(b.textContent.trim()==='编辑'&&!b.dataset.sid){
|
||
b.dataset.sid='1';
|
||
var par=b.parentElement;
|
||
if(!par.classList.contains('si-row-actions'))par.classList.add('si-row-actions');
|
||
var plusInSection=par.querySelector('.si-plus');
|
||
if(plusInSection)plusInSection.remove();
|
||
var del=document.createElement('button');
|
||
del.className='si-del';
|
||
del.textContent='删除';
|
||
(function(editBtn){
|
||
del.onclick=function(e){
|
||
e.stopPropagation();e.preventDefault();
|
||
var row=editBtn.closest('[class]');
|
||
var txt=row?row.textContent:'';
|
||
var m=txt.match(/([\d]+\.[\d]+|appendix-[\w]+|preface|epilogue)/);
|
||
var sid=m?m[0]:'';
|
||
var name=txt.substring(0,40).replace(/读取|编辑|删除|免费|付费|¥[\d.]+|\+/g,'').trim();
|
||
if(!confirm('确定删除「'+name+'」'+(sid?' (ID:'+sid+')':'')+' ?'))return;
|
||
auth().then(function(ok){
|
||
if(!ok){toast('认证失败',false);return}
|
||
apicall('DELETE','/api/admin/content/'+(sid||name)).then(function(r){
|
||
if(r.success!==false){toast('已删除');setTimeout(function(){location.reload()},800)}
|
||
else{
|
||
apicall('DELETE','/api/db/book?action=delete&id='+(sid||name)).then(function(r2){
|
||
if(r2.success!==false){toast('已删除');setTimeout(function(){location.reload()},800)}
|
||
else toast('删除失败: '+(r2.error||r.error||''),false)
|
||
})
|
||
}
|
||
})
|
||
})
|
||
}
|
||
})(b);
|
||
par.appendChild(del);
|
||
addFreeToggle(b);
|
||
}
|
||
}
|
||
}
|
||
function addChapterPlus(){
|
||
var seen=new Set();
|
||
var rows=document.querySelectorAll('[class]');
|
||
for(var i=0;i<rows.length;i++){
|
||
var r=rows[i];
|
||
if(r.querySelector('.si-chap-plus')||seen.has(r))continue;
|
||
var t=(r.textContent||'').trim();
|
||
if((/序言|附录|尾声|第一篇|第二篇|第三篇|第四篇|第五篇/.test(t)&&/\d+节/.test(t))){
|
||
seen.add(r);
|
||
r.dataset.draggableItem='chapter';
|
||
var plus=document.createElement('button');
|
||
plus.className='si-plus si-chap-plus';plus.textContent='+';plus.title='在此章节下新建小节';
|
||
plus.onclick=function(e){e.stopPropagation();e.preventDefault();
|
||
var info=getSectionInfo(this.parentElement);
|
||
togglePanel('upload',info||{});
|
||
};
|
||
r.style.display=r.style.display||'flex';r.style.alignItems='center';
|
||
r.appendChild(plus);
|
||
}
|
||
}
|
||
}
|
||
function addDragDrop(){
|
||
var items=document.querySelectorAll('[data-draggable-item]');
|
||
items.forEach(function(el){if(el.dataset.siDrag)return;el.dataset.siDrag='1';
|
||
el.draggable=true;el.style.cursor='grab';
|
||
el.addEventListener('dragstart',onDragStart);
|
||
el.addEventListener('dragover',onDragOver);el.addEventListener('drop',onDrop);
|
||
});
|
||
var sect=document.querySelectorAll('button');
|
||
for(var j=0;j<sect.length;j++){
|
||
var sb=sect[j];
|
||
if(sb.textContent.trim()==='编辑'){
|
||
var row=sb.closest('[class]');
|
||
if(row&&!row.dataset.siDrag){
|
||
row.draggable=true;row.dataset.siDrag='1';row.dataset.draggableItem='section';
|
||
row.style.cursor='grab';
|
||
row.addEventListener('dragstart',onDragStart);
|
||
row.addEventListener('dragover',onDragOver);
|
||
row.addEventListener('drop',onDrop);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var dragEl=null;
|
||
function onDragStart(e){dragEl=e.currentTarget;e.dataTransfer.effectAllowed='move';
|
||
e.dataTransfer.setData('text/plain','');e.currentTarget.classList.add('si-dragging');}
|
||
function onDragOver(e){e.preventDefault();e.dataTransfer.dropEffect='move';
|
||
var t=e.currentTarget;
|
||
if(t!==dragEl){t.classList.add('si-drop-target');
|
||
var sibs=t.parentElement?t.parentElement.children:[];
|
||
for(var k=0;k<sibs.length;k++){if(sibs[k]!==t)sibs[k].classList.remove('si-drop-target')}
|
||
}}
|
||
function onDrop(e){e.preventDefault();
|
||
document.querySelectorAll('.si-drop-target').forEach(function(x){x.classList.remove('si-drop-target')});
|
||
if(!dragEl)return;
|
||
dragEl.classList.remove('si-dragging');
|
||
var dest=e.currentTarget;
|
||
if(dest!==dragEl&&dest.parentNode===dragEl.parentNode){
|
||
var par=dest.parentNode;
|
||
var list=Array.from(par.children).filter(function(c){return c.dataset.siDrag||c.draggable;});
|
||
var i0=list.indexOf(dragEl),i1=list.indexOf(dest);
|
||
if(i0>=0&&i1>=0&&i0!==i1){
|
||
if(i0<i1)par.insertBefore(dragEl,dest.nextSibling);
|
||
else par.insertBefore(dragEl,dest);
|
||
var newList=Array.from(par.children).filter(function(c){return c.dataset.siDrag||c.draggable;});
|
||
var ids=newList.map(function(x){return(x.textContent.match(/([\d]+\.[\d]+|appendix-[\w-]+|preface|epilogue)/)||[])[1]}).filter(Boolean);
|
||
if(ids.length>0)auth().then(function(ok){
|
||
if(ok)apicall('POST','/api/db/book/order',{ids:ids}).then(function(r){if(r&&r.success)toast('已排序');else toast('排序已更新(后端接口可后续对接)',false)})
|
||
});
|
||
}
|
||
}
|
||
dragEl=null;
|
||
}
|
||
document.addEventListener('dragend',function(){document.querySelectorAll('.si-dragging,.si-drop-target').forEach(function(x){x.classList.remove('si-dragging','si-drop-target')});dragEl=null});
|
||
|
||
function addFreeToggle(editBtn){
|
||
var row=editBtn.closest('[class]');
|
||
if(!row||row.querySelector('.si-free-toggle'))return;
|
||
var sid=(row.textContent.match(/([\d]+\.[\d]+|appendix-[\w-]+|preface|epilogue)/)||[])[1]||'';
|
||
var candidates=row.querySelectorAll('span, div, [class]');
|
||
for(var j=0;j<candidates.length;j++){
|
||
var el=candidates[j];
|
||
if(el.classList&&el.classList.contains('si-free-toggle'))continue;
|
||
var t=(el.textContent||'').trim();
|
||
if((t==='免费'||/^¥[\d.]+$/.test(t))&&el.children.length===0){
|
||
var isFree=t==='免费';
|
||
var toggle=document.createElement('span');
|
||
toggle.className='si-free-toggle'+(isFree?'':' paid');
|
||
toggle.textContent=isFree?'免费':'付费';
|
||
toggle.dataset.sectionId=sid;
|
||
toggle.dataset.price=isFree?'0':'1';
|
||
toggle.onclick=function(e){e.stopPropagation();e.preventDefault();
|
||
if(e.detail>=2)return;
|
||
var sectionId=toggle.dataset.sectionId;
|
||
if(!sectionId){toast('无法识别章节ID',false);return}
|
||
var toFree=toggle.textContent==='付费';
|
||
auth().then(function(ok){
|
||
if(!ok){toast('认证失败',false);return}
|
||
var pr=toFree?0:1;
|
||
apicall('POST','/api/db/book',{id:sectionId,isFree:toFree,price:pr}).then(function(r){
|
||
if(r.success!==false){toggle.textContent=toFree?'免费':'¥'+pr;toggle.classList.toggle('paid',!toFree);toggle.dataset.price=pr;toast('已更新')}
|
||
else toast('更新失败: '+(r.error||''),false)
|
||
})
|
||
})
|
||
};
|
||
toggle.ondblclick=function(e){e.stopPropagation();e.preventDefault();
|
||
var sectionId=toggle.dataset.sectionId;
|
||
if(!sectionId){toast('无法识别章节ID',false);return}
|
||
if(toggle.textContent==='免费'){
|
||
auth().then(function(ok){
|
||
if(!ok){toast('认证失败',false);return}
|
||
var pr=parseFloat(prompt('请输入付费金额','1'))||1;
|
||
apicall('POST','/api/db/book',{id:sectionId,isFree:false,price:pr}).then(function(r){
|
||
if(r.success!==false){toggle.textContent='¥'+pr;toggle.classList.add('paid');toggle.dataset.price=pr;toast('已更新')}
|
||
else toast('更新失败',false)
|
||
})
|
||
})
|
||
}else{
|
||
auth().then(function(ok){
|
||
if(!ok){toast('认证失败',false);return}
|
||
apicall('POST','/api/db/book',{id:sectionId,isFree:true,price:0}).then(function(r){
|
||
if(r.success!==false){toggle.textContent='免费';toggle.classList.remove('paid');toggle.dataset.price='0';toast('已设为免费')}
|
||
else toast('更新失败',false)
|
||
})
|
||
})
|
||
}
|
||
};
|
||
el.parentNode.replaceChild(toggle,el);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
function siUpload(){
|
||
var title=document.getElementById('si-utitle').value.trim();
|
||
var content=document.getElementById('si-ucontent').value.trim();
|
||
if(!title){toast('请填写标题',false);return}
|
||
if(!content){toast('请填写内容',false);return}
|
||
var imgs=document.getElementById('si-uimgs').value.trim().split('\n').filter(Boolean);
|
||
imgs.forEach(function(u,i){content=content.replace('{{image_'+(i+1)+'}}','+')')});
|
||
var price=parseFloat(document.getElementById('si-uprice').value)||0;
|
||
var data={
|
||
id:document.getElementById('si-uid').value.trim()||undefined,
|
||
title:title,content:content,price:price,isFree:price===0,
|
||
partId:document.getElementById('si-upart').value,
|
||
chapterId:document.getElementById('si-uchap').value
|
||
};
|
||
toast('上传中...');
|
||
auth().then(function(ok){
|
||
if(!ok){toast('认证失败',false);return}
|
||
apicall('POST','/api/db/book',data).then(function(r){
|
||
if(r.success!==false){
|
||
toast('上传成功!');
|
||
document.getElementById('si-utitle').value='';
|
||
document.getElementById('si-ucontent').value='';
|
||
document.getElementById('si-uimgs').value='';
|
||
document.getElementById('si-uid').value='';
|
||
setTimeout(function(){location.reload()},1000)
|
||
}else toast('失败: '+(r.error||''),false)
|
||
})
|
||
})
|
||
}
|
||
|
||
setInterval(run,500);
|
||
new MutationObserver(run).observe(document.getElementById('root'),{childList:true,subtree:true});
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|