From 0a5b593cb4a8fcf5cb0f126ea765dd9248d29e86 Mon Sep 17 00:00:00 2001 From: selfrelease Date: Mon, 31 Aug 2026 16:13:21 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=B8=93=E5=AE=B6=E5=88=9B?= =?UTF-8?q?=E5=BB=BA/=E7=BC=96=E8=BE=91=E5=90=88=E5=B9=B6=E4=B8=BAExpertFo?= =?UTF-8?q?rmDialog,=E5=88=9B=E5=BB=BA=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=A4=B4=E5=83=8F=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web2/src/pages/admin/AdminExperts.tsx | 245 +++++++++----------------- 1 file changed, 80 insertions(+), 165 deletions(-) diff --git a/web2/src/pages/admin/AdminExperts.tsx b/web2/src/pages/admin/AdminExperts.tsx index 0f4d5b3..fe38dbd 100644 --- a/web2/src/pages/admin/AdminExperts.tsx +++ b/web2/src/pages/admin/AdminExperts.tsx @@ -165,20 +165,12 @@ export function AdminExperts() { )} - {/* 编辑弹窗 */} - {editing && ( - setEditing(null)} - onSuccess={() => { loadExperts(); setEditing(null) }} - /> - )} - - {/* 新增专家弹窗 */} - {creating && ( - setCreating(false)} - onSuccess={() => { loadExperts(); setCreating(false) }} + onClose={() => { setEditing(null); setCreating(false) }} + onSuccess={() => { loadExperts(); setEditing(null); setCreating(false) }} /> )} @@ -204,40 +196,51 @@ export function AdminExperts() { ) } -/** 编辑专家信息弹窗:含头像上传 + 表单字段 */ -function EditExpertDialog({ +/** 专家信息弹窗:创建/编辑共用,含头像上传 + 表单字段 */ +function ExpertFormDialog({ expert, onClose, onSuccess, }: { - expert: Expert + expert: Expert | null // null = 创建模式 onClose: () => void onSuccess: () => void }) { - const [name, setName] = useState(expert.name) - const [category, setCategory] = useState(expert.category) - const [title, setTitle] = useState(expert.title) - const [bio, setBio] = useState(expert.bio) - const [expertise, setExpertise] = useState(expert.expertise) - const [avatarUrl, setAvatarUrl] = useState(expert.avatar_url) + const isCreate = !expert + const [name, setName] = useState(expert?.name || '') + const [phone, setPhone] = useState('') + const [password, setPassword] = useState('') + const [category, setCategory] = useState(expert?.category || 'legal') + const [title, setTitle] = useState(expert?.title || '') + const [bio, setBio] = useState(expert?.bio || '') + const [expertise, setExpertise] = useState(expert?.expertise || '') + const [avatarUrl, setAvatarUrl] = useState(expert?.avatar_url || null) const [saving, setSaving] = useState(false) const [uploading, setUploading] = useState(false) const fileInputRef = useRef(null) + const [pendingFile, setPendingFile] = useState(null) + const handleUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (!file) return - setUploading(true) - try { - const res = await api.adminUploadExpertAvatar(expert.id, file) - setAvatarUrl(res.avatar_url) - toast.success('头像上传成功') - } catch (err) { - toast.error(err instanceof Error ? err.message : '上传失败') - } finally { - setUploading(false) - // 清空 input 允许重复选同一文件 - if (fileInputRef.current) fileInputRef.current.value = '' + if (expert) { + // 编辑模式:直接上传 + setUploading(true) + try { + const res = await api.adminUploadExpertAvatar(expert.id, file) + setAvatarUrl(res.avatar_url) + toast.success('头像上传成功') + } catch (err) { + toast.error(err instanceof Error ? err.message : '上传失败') + } finally { + setUploading(false) + if (fileInputRef.current) fileInputRef.current.value = '' + } + } else { + // 创建模式:本地预览,保存时上传 + setPendingFile(file) + setAvatarUrl(URL.createObjectURL(file)) } } @@ -248,21 +251,34 @@ function EditExpertDialog({ } const handleSave = async () => { - if (!name.trim()) { - toast.error('姓名不能为空') - return + if (!name.trim()) { toast.error('姓名不能为空'); return } + if (isCreate) { + if (!phone.trim()) { toast.error('手机号不能为空'); return } + if (password.length < 6) { toast.error('密码至少 6 位'); return } } setSaving(true) try { - await api.adminUpdateExpert(expert.id, { - name: name.trim(), - category, - title: title.trim(), - bio: bio.trim(), - expertise: expertise.trim(), - avatar_url: avatarUrl, - }) - toast.success('专家信息已更新') + if (isCreate) { + const res = await api.adminCreateExpert({ + name: name.trim(), phone: phone.trim(), password, + category, title: title.trim(), bio: bio.trim(), + expertise: expertise.trim(), status: 'active', + }) + // 创建成功后上传头像 + if (pendingFile && res.expert.id) { + try { + await api.adminUploadExpertAvatar(res.expert.id, pendingFile) + } catch { /* 头像失败不影响创建 */ } + } + toast.success('专家创建成功') + } else { + await api.adminUpdateExpert(expert!.id, { + name: name.trim(), category, + title: title.trim(), bio: bio.trim(), + expertise: expertise.trim(), avatar_url: avatarUrl, + }) + toast.success('专家信息已更新') + } onSuccess() } catch (err) { toast.error(err instanceof Error ? err.message : '保存失败') @@ -275,14 +291,14 @@ function EditExpertDialog({ !o && onClose()}> - 编辑专家信息 - 修改专家资料、上传头像 + {isCreate ? '新增专家' : '编辑专家信息'} + {isCreate ? '创建专家账号,填写基本信息和登录密码' : '修改专家资料、上传头像'}
{/* 头像上传区 */}
- +

专家头像

支持 JPG / PNG / WebP,最大 5MB

@@ -324,6 +340,20 @@ function EditExpertDialog({ setName(e.target.value)} placeholder="专家姓名" />
+ {/* 手机号 + 密码(仅创建模式) */} + {isCreate && ( + <> +
+ + setPhone(e.target.value)} placeholder="如:13800138000" /> +
+
+ + setPassword(e.target.value)} placeholder="设置初始密码" /> +
+ + )} + {/* 分类 */}
@@ -361,123 +391,8 @@ function EditExpertDialog({ - - -
- ) -} - -/** 新增专家弹窗:含登录账号 + 基本信息 */ -function CreateExpertDialog({ - onClose, - onSuccess, -}: { - onClose: () => void - onSuccess: () => void -}) { - const [name, setName] = useState('') - const [phone, setPhone] = useState('') - const [password, setPassword] = useState('') - const [category, setCategory] = useState('legal') - const [title, setTitle] = useState('') - const [bio, setBio] = useState('') - const [expertise, setExpertise] = useState('') - const [saving, setSaving] = useState(false) - - const handleSave = async () => { - if (!name.trim()) { toast.error('姓名不能为空'); return } - if (!phone.trim()) { toast.error('手机号不能为空'); return } - if (password.length < 6) { toast.error('密码至少 6 位'); return } - setSaving(true) - try { - await api.adminCreateExpert({ - name: name.trim(), - phone: phone.trim(), - password, - category, - title: title.trim(), - bio: bio.trim(), - expertise: expertise.trim(), - status: 'active', - }) - toast.success('专家创建成功') - onSuccess() - } catch (err) { - toast.error(err instanceof Error ? err.message : '创建失败') - } finally { - setSaving(false) - } - } - - return ( - !o && onClose()}> - - - 新增专家 - 创建专家账号,填写基本信息和登录密码 - - -
- {/* 姓名 */} -
- - setName(e.target.value)} placeholder="专家姓名" /> -
- - {/* 手机号 */} -
- - setPhone(e.target.value)} placeholder="如:13800138000" /> -
- - {/* 密码 */} -
- - setPassword(e.target.value)} placeholder="设置初始密码" /> -
- - {/* 分类 */} -
- - -
- - {/* 职称 */} -
- - setTitle(e.target.value)} placeholder="如:高级律师" /> -
- - {/* 简介 */} -
- -