fix: 空评论列表 500,并完善撤回审计与角色说明

空切片 JSON 统一为数组;撤回保留正文供站长阅读,成员侧居中提示与重新编辑;头像支持拖放;角色弹窗补全职责说明并汇入封禁。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-16 00:23:55 +08:00
parent 42c395a864
commit 8c5de13899
29 changed files with 835 additions and 405 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useState } from "react";
import { useCallback, useState } from "react";
import { useRouter } from "next/navigation";
import { Check } from "lucide-react";
import { apiUpdateProfile, type User } from "@/lib/api";
@@ -23,6 +23,34 @@ export default function ProfileForm({ user }: ProfileFormProps) {
const [submitting, setSubmitting] = useState(false);
const [error, setError] = useState("");
const [success, setSuccess] = useState("");
const [avatarDragOver, setAvatarDragOver] = useState(false);
const [incomingAvatarFile, setIncomingAvatarFile] = useState<File | null>(null);
const clearIncomingAvatar = useCallback(() => setIncomingAvatarFile(null), []);
const handleAvatarDragOver = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
if (e.dataTransfer.types.includes("Files")) setAvatarDragOver(true);
};
const handleAvatarDragLeave = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
// 仅在离开整块卡片时取消高亮(避免子元素冒泡闪烁)
const related = e.relatedTarget as Node | null;
if (related && e.currentTarget.contains(related)) return;
setAvatarDragOver(false);
};
const handleAvatarDrop = (e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();
setAvatarDragOver(false);
const file = e.dataTransfer.files?.[0];
if (!file) return;
setIncomingAvatarFile(file);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
@@ -72,20 +100,32 @@ export default function ProfileForm({ user }: ProfileFormProps) {
)}
{error && <div className="alert-error" role="alert">{error}</div>}
{/* 头像:点击上传/裁剪,或从历史图片中选择 */}
{/* 头像:点击上传/裁剪,或拖放图片到本区域 */}
<div
className="flex items-center gap-4 p-4 rounded-xl"
style={{ background: "var(--panel-2)", border: "1px solid var(--line)" }}
className="flex items-center gap-4 p-4 rounded-xl transition-colors"
style={{
background: avatarDragOver
? "color-mix(in srgb, var(--accent) 12%, var(--panel-2))"
: "var(--panel-2)",
border: `1px solid ${avatarDragOver ? "var(--accent)" : "var(--line)"}`,
}}
onDragOver={handleAvatarDragOver}
onDragLeave={handleAvatarDragLeave}
onDrop={handleAvatarDrop}
>
<AvatarPicker
name={nickname || user.username}
src={user.avatar || ""}
size={56}
incomingFile={incomingAvatarFile}
onIncomingConsumed={clearIncomingAvatar}
/>
<div className="flex-1 min-w-0">
<p className="text-[13.5px] font-bold" style={{ color: "var(--ink)" }}>头像</p>
<p className="text-[12px] mt-1" style={{ color: "var(--ink-3)" }}>
点击头像上传新图片,支持拖动裁剪;统一转为 WebP,裁剪后不超过 2MB
{avatarDragOver
? "松开即可上传并裁剪"
: "点击头像或拖放图片到此处;支持拖动裁剪,统一转为 WebP,裁剪后不超过 2MB"}
</p>
</div>
</div>