fix(账号): 固化超级管理员身份

This commit is contained in:
Script Generator
2026-06-26 11:18:00 +08:00
parent 56e0fe562d
commit 4cda624dc7
13 changed files with 419 additions and 80 deletions

View File

@@ -3,8 +3,10 @@
import { useEffect, useMemo, useState } from 'react';
import { Plus, Pencil, Trash2, X, ChevronRight, FolderOpen, Settings } from 'lucide-react';
import { useMemberStore } from '@/stores/useMemberStore';
import { useAuthStore } from '@/stores/useAuthStore';
import { maskPhone, generatePassword } from '@/lib/members';
import type { Member, Department, PasswordRule } from '@/lib/members';
import { isSystemAdminMember } from '@/lib/member-system';
import { RouteGuard } from '@/components/auth/Guard';
export default function MembersPage() {
@@ -17,6 +19,8 @@ export default function MembersPage() {
function MembersPageContent() {
const { departments, members, roles, passwordRule, fetchMembers, updatePasswordRule, createDepartment, updateDepartment, deleteDepartment, createMember, updateMember, deleteMember } = useMemberStore();
const currentUser = useAuthStore((s) => s.user);
const refreshUser = useAuthStore((s) => s.refreshUser);
const [activeDeptId, setActiveDeptId] = useState<string | null>(null);
const [showMemberModal, setShowMemberModal] = useState(false);
const [editingMember, setEditingMember] = useState<Member | null>(null);
@@ -169,7 +173,12 @@ function MembersPageContent() {
<td className="px-4 py-3 text-right">
<div className="flex items-center justify-end gap-1">
<button onClick={() => { setEditingMember(m); setShowMemberModal(true); }} className="h-6 px-2 rounded text-[11px] font-medium text-[var(--ink-soft)] hover:bg-[var(--bg-subtle)]"></button>
<button onClick={() => deleteMember(m.id)} className="h-6 px-2 rounded text-[11px] font-medium text-red-500 hover:bg-red-50"></button>
{!isSystemAdminMember(m) && (
<button onClick={() => deleteMember(m.id)} className="h-6 px-2 rounded text-[11px] font-medium text-red-500 hover:bg-red-50"></button>
)}
{isSystemAdminMember(m) && (
<span className="h-6 px-2 inline-flex items-center rounded text-[11px] font-medium text-amber-700 bg-amber-50"></span>
)}
</div>
</td>
</tr>
@@ -191,8 +200,12 @@ function MembersPageContent() {
passwordRule={passwordRule}
onClose={() => setShowMemberModal(false)}
onSubmit={(data) => {
if (editingMember) updateMember(editingMember.id, data);
else createMember(data as any);
if (editingMember) {
updateMember(editingMember.id, data);
if (editingMember.id === currentUser?.id) void refreshUser();
} else {
createMember(data as any);
}
setShowMemberModal(false);
}}
/>
@@ -239,6 +252,7 @@ function MemberModal({ initial, departments, roles, defaultDeptId, passwordRule,
const [phone, setPhone] = useState(initial?.phone ?? '');
const [email, setEmail] = useState(initial?.email ?? '');
const [password] = useState(() => initial?.password ?? generatePassword(passwordRule));
const isSystemMember = initial ? isSystemAdminMember(initial) : false;
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
@@ -254,21 +268,26 @@ function MemberModal({ initial, departments, roles, defaultDeptId, passwordRule,
<button onClick={onClose} className="p-1 rounded hover:bg-[var(--bg-subtle)] text-[var(--ink-muted)]"><X className="h-4 w-4" /></button>
</div>
<form onSubmit={handleSubmit} className="space-y-3">
{isSystemMember && (
<div className="rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-[11px] text-amber-700">
</div>
)}
<div>
<label className="text-[12px] font-medium text-[var(--ink-soft)] mb-1 block"> *</label>
<input value={name} onChange={(e) => setName(e.target.value)} required placeholder="请输入姓名" className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg-card)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none" />
<input value={name} onChange={(e) => setName(e.target.value)} required disabled={isSystemMember} placeholder="请输入姓名" className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg-card)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none disabled:bg-zinc-50 disabled:text-[var(--ink-soft)]" />
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-[12px] font-medium text-[var(--ink-soft)] mb-1 block"> *</label>
<select value={departmentId} onChange={(e) => setDepartmentId(e.target.value)} required className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg-card)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none">
<select value={departmentId} onChange={(e) => setDepartmentId(e.target.value)} required disabled={isSystemMember} className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg-card)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none disabled:bg-zinc-50 disabled:text-[var(--ink-soft)]">
<option value=""></option>
{departments.map((d) => <option key={d.id} value={d.id}>{d.parentId ? ' ' : ''}{d.name}</option>)}
</select>
</div>
<div>
<label className="text-[12px] font-medium text-[var(--ink-soft)] mb-1 block"> *</label>
<select value={roleId} onChange={(e) => setRoleId(e.target.value)} required className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg-card)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none">
<select value={roleId} onChange={(e) => setRoleId(e.target.value)} required disabled={isSystemMember} className="h-9 w-full rounded-lg border border-[var(--line)] bg-[var(--bg-card)] px-3 text-[13px] focus:border-[var(--accent)] focus:outline-none disabled:bg-zinc-50 disabled:text-[var(--ink-soft)]">
<option value=""></option>
{roles.map((r) => <option key={r.id} value={r.id}>{r.name}</option>)}
</select>