From 37e26be04bcf6e2f837c237101d06e6328375017 Mon Sep 17 00:00:00 2001 From: ylhan Date: Tue, 22 Sep 2026 00:27:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=A1=B5=E9=9D=A2=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=8B=96=E6=8B=BD=E6=8E=92=E5=BA=8F(order?= =?UTF-8?q?=E5=AD=97=E6=AE=B5+order=E6=8E=A5=E5=8F=A3),=20=E6=8C=81?= =?UTF-8?q?=E4=B9=85=E5=8C=96=E5=88=B0=E5=90=8E=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xinquan/app.py | 57 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/xinquan/app.py b/xinquan/app.py index e3aac8f..f4836ee 100644 --- a/xinquan/app.py +++ b/xinquan/app.py @@ -132,7 +132,10 @@ INDEX_HTML = r""" button:hover,.btn:hover{opacity:.9} .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:14px} .tile{background:var(--card);border:1px solid var(--border);border-radius:16px;padding:16px; - text-decoration:none;color:var(--text);display:block;transition:.16s;box-shadow:0 2px 10px rgba(20,30,70,.05)} + text-decoration:none;color:var(--text);display:block;transition:.16s;box-shadow:0 2px 10px rgba(20,30,70,.05); + cursor:grab} + .tile:active{cursor:grabbing} + .tile.drag-over{border-color:var(--brand);box-shadow:0 0 0 2px rgba(99,102,241,.25)} .tile:hover{transform:translateY(-2px);box-shadow:0 10px 24px rgba(20,30,70,.1)} .tile h3{font-size:16px;margin-bottom:6px} .tile p{font-size:13px;color:var(--muted);line-height:1.5; @@ -218,13 +221,32 @@ function renderList(){ `; const a=$('#app'); if(!state.pages.length){ a.innerHTML=`
还没有内容。
`; return; } - a.innerHTML = `
${state.pages.map(p=>` - + a.innerHTML = ``; } +let dragId=null; +function dragStart(id){ dragId=id; } +function dropEv(ev){ + ev.preventDefault(); + if(!dragId) return; + const tile=ev.target.closest('.tile'); + if(!tile) return; + const tid=tile.getAttribute('data-id'); + if(!tid || tid===dragId){ dragId=null; return; } + const from=state.pages.findIndex(p=>p.id===dragId); + const to=state.pages.findIndex(p=>p.id===tid); + if(from<0 || to<0){ dragId=null; return; } + const [moved]=state.pages.splice(from,1); + state.pages.splice(to,0,moved); + dragId=null; + renderList(); + api('/api/pages/order',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({order:state.pages.map(p=>p.id)})}) + .catch(e=>toast('排序保存失败: '+e.message)); +} async function openPage(id){ try{ const p = await api('/api/pages/'+encodeURIComponent(id)); @@ -475,6 +497,8 @@ class Handler(BaseHTTPRequestHandler): return self._logout() if not self._user(): return self._auth_denied() + if path == "/api/pages/order": + return self._reorder() if path == "/api/pages": return self._create_page() if path == "/api/upload": @@ -514,9 +538,10 @@ class Handler(BaseHTTPRequestHandler): pages = _read_pages() out = [{"id": pid, "title": p.get("title", ""), "summary": _summary(p.get("content", "")), - "updated": p.get("updated")} + "updated": p.get("updated"), + "order": p.get("order", 1 << 30)} for pid, p in pages.items()] - out.sort(key=lambda x: x["updated"] or "", reverse=True) + out.sort(key=lambda x: x.get("order", 1 << 30)) self._json(out) def _get_page(self, pid): @@ -535,9 +560,10 @@ class Handler(BaseHTTPRequestHandler): pages = _read_pages() pid = uuid.uuid4().hex[:12] now = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()) + nxt = max((p.get("order", 0) for p in pages.values()), default=-1) + 1 pages[pid] = {"title": data["title"].strip(), "content": data.get("content", ""), - "created": now, "updated": now} + "created": now, "updated": now, "order": nxt} _write_pages(pages) self._json({"id": pid}, 201) @@ -566,6 +592,25 @@ class Handler(BaseHTTPRequestHandler): _write_pages(pages) self._json({"ok": True}) + def _reorder(self): + data = self._read_json() + order = data.get("order") if isinstance(data, dict) else None + if not isinstance(order, list): + return self._json({"error": "bad order"}, 400) + with _lock: + pages = _read_pages() + for i, pid in enumerate(order): + if pid in pages: + pages[pid]["order"] = i + # 未在列表中的页面补一个靠后的 order + have = set(order) + big = len(order) + for pid in pages: + if pid not in have: + pages[pid]["order"] = pages[pid].get("order", big) + _write_pages(pages) + self._json({"ok": True}) + # ---- 图片 ---- def _upload(self): data = self._read_json()