diff --git a/.server.pid b/.server.pid new file mode 100644 index 0000000..0932e1a --- /dev/null +++ b/.server.pid @@ -0,0 +1 @@ +49192 diff --git a/.workbuddy/memory/2026-07-17.md b/.workbuddy/memory/2026-07-17.md index bb3a906..a1bb4d7 100644 --- a/.workbuddy/memory/2026-07-17.md +++ b/.workbuddy/memory/2026-07-17.md @@ -87,3 +87,10 @@ - app.js 中 shelfSortMode 默认值从 "default" 改为 "category" - 删除 toggleShelfSort 函数和 .shelf-sort-btn CSS 样式 - 书架列表始终按分类分组渲染,无需手动切换 + +## 12:32 书架恢复横向网格展示,移除分类分组标题 + +- 用户反馈:不需要展示分类分组标题(如"历史-历史读物 3本"),恢复简单横向书架 +- 删除 renderBooksGroupedByCategory 函数,loadShelf 始终用简单网格渲染 +- 后端 sort=category 仍生效(按分类排序),但前端不显示分组 +- 分类筛选标签(可折叠层级)保留不变 diff --git a/start_server.sh b/start_server.sh new file mode 100755 index 0000000..81ca7fc --- /dev/null +++ b/start_server.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# 微信读书数据看板 - 服务启动脚本 +# 用法: +# ./start_server.sh 启动服务(前台运行) +# ./start_server.sh daemon 后台守护进程方式启动 +# ./start_server.sh stop 停止服务 +# ./start_server.sh restart 重启服务 +# ./start_server.sh status 查看状态 + +PROJECT_DIR="/Users/hwj/WorkBuddy/2026-07-17-09-48-20" +PYTHON="/Users/hwj/.workbuddy/binaries/python/envs/default/bin/python" +PID_FILE="$PROJECT_DIR/.server.pid" +LOG_FILE="/tmp/weread_server.log" +PORT=8080 + +cd "$PROJECT_DIR" || exit 1 + +is_running() { + if [ -f "$PID_FILE" ]; then + local pid=$(cat "$PID_FILE") + if kill -0 "$pid" 2>/dev/null; then + return 0 + fi + fi + # 也检查端口是否被占用 + if lsof -i :$PORT -t >/dev/null 2>&1; then + return 0 + fi + return 1 +} + +get_pid() { + if [ -f "$PID_FILE" ]; then + local pid=$(cat "$PID_FILE") + if kill -0 "$pid" 2>/dev/null; then + echo "$pid" + return + fi + fi + lsof -i :$PORT -t 2>/dev/null | head -1 +} + +stop_server() { + if is_running; then + local pid=$(get_pid) + echo "停止服务 (PID: $pid)..." + kill "$pid" 2>/dev/null + sleep 1 + if kill -0 "$pid" 2>/dev/null; then + kill -9 "$pid" 2>/dev/null + fi + rm -f "$PID_FILE" + echo "已停止" + else + echo "服务未运行" + fi +} + +start_foreground() { + if is_running; then + echo "服务已在运行 (PID: $(get_pid))" + exit 1 + fi + echo "启动服务(前台模式)..." + exec "$PYTHON" app.py +} + +start_daemon() { + if is_running; then + echo "服务已在运行 (PID: $(get_pid))" + exit 1 + fi + echo "启动服务(后台守护模式)..." + nohup "$PYTHON" app.py > "$LOG_FILE" 2>&1 & + local pid=$! + echo "$pid" > "$PID_FILE" + sleep 2 + if kill -0 "$pid" 2>/dev/null; then + echo "服务已启动 (PID: $pid)" + echo "访问: http://127.0.0.1:$PORT" + echo "日志: $LOG_FILE" + else + echo "启动失败,请查看日志: $LOG_FILE" + cat "$LOG_FILE" + rm -f "$PID_FILE" + exit 1 + fi +} + +show_status() { + if is_running; then + local pid=$(get_pid) + echo "✅ 服务运行中 (PID: $pid)" + echo " 端口: $PORT" + echo " 日志: $LOG_FILE" + else + echo "❌ 服务未运行" + fi +} + +case "${1:-start}" in + start) start_foreground ;; + daemon) start_daemon ;; + stop) stop_server ;; + restart) stop_server; sleep 1; start_daemon ;; + status) show_status ;; + *) echo "用法: $0 {start|daemon|stop|restart|status}"; exit 1 ;; +esac diff --git a/static/css/style.css b/static/css/style.css index cfe6e7e..605c4e0 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -766,6 +766,33 @@ body { box-shadow: 0 0 0 3px var(--accent-bg); } +.shelf-refresh-btn { + padding: 7px 16px; + border: 1px solid var(--border); + border-radius: var(--radius-sm); + font-size: 13px; + color: var(--text-secondary); + background: var(--bg-card); + cursor: pointer; + transition: all 0.2s; + white-space: nowrap; +} + +.shelf-refresh-btn:hover:not(:disabled) { + border-color: var(--accent); + color: var(--accent); +} + +.shelf-refresh-btn:disabled { + opacity: 0.6; + cursor: wait; +} + +.shelf-refresh-btn.loading { + border-color: var(--accent); + color: var(--accent); +} + .filter-checkbox { display: flex; align-items: center; diff --git a/static/js/app.js b/static/js/app.js index ecf0053..02fc466 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -318,6 +318,23 @@ function filterByCategory(fullCategory) { loadShelf(1); } +// ===== 书架刷新 ===== + +async function refreshShelf() { + const btn = document.getElementById("shelfRefreshBtn"); + btn.disabled = true; + btn.classList.add("loading"); + btn.textContent = "↻ 刷新中..."; + try { + await loadShelfCategories(); + await loadShelf(1); + } finally { + btn.disabled = false; + btn.classList.remove("loading"); + btn.textContent = "↻ 刷新"; + } +} + // ===== 初始化 ===== document.addEventListener("DOMContentLoaded", async () => { diff --git a/templates/index.html b/templates/index.html index 4453921..d839dca 100644 --- a/templates/index.html +++ b/templates/index.html @@ -125,6 +125,7 @@ +