fix: 修复后台文章计数为0 + 分类编辑补全描述字段 + CSP放行外部图片 + 更新关于页描述

- 文章管理:计数请求补 page=1 参数,命中分页接口返回正确的 total
- 分类管理:编辑模式新增描述输入框,保存时一并提交 description
- CSP:img-src 加入 https: 允许加载外部图片
- 关于页:数据存储描述从 JSON 文件更正为 SQLite 数据库
- Footer:添加 ICP 备案号
This commit is contained in:
胡旭
2026-06-24 13:51:48 +08:00
parent 3707eddfd4
commit 18e915bcbb
69 changed files with 6818 additions and 1422 deletions
+38
View File
@@ -0,0 +1,38 @@
"use client";
import { useEffect, useRef } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
/**
* GSAP 动画的统一入口。
*
* 核心原则:
* 1. 尊重 prefers-reduced-motion —— 开启时直接跳过动画,内容保持可见。
* 2. 在 gsap.context 内执行,卸载时自动 revert,避免泄露。
*
* 用法:
* const ref = useGsapAnimation<HTMLElement>((scope, isReduced) => {
* if (isReduced) return;
* gsap.from(".item", { y: 40, opacity: 0, stagger: 0.1, scrollTrigger: {...} });
* });
*/
export function useGsapAnimation<T extends HTMLElement = HTMLElement>(
setup: (scope: T, isReduced: boolean) => void,
deps: unknown[] = []
) {
const ref = useRef<T>(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const isReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const ctx = gsap.context(() => setup(el, isReduced), el);
return () => ctx.revert();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
return ref;
}