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

@@ -6,6 +6,7 @@ import { useBugStore } from '@/stores/useBugStore';
import { useTestCaseStore } from '@/stores/useTestCaseStore';
import { useRequirementStore } from '@/stores/useRequirementStore';
import { useAuthStore } from '@/stores/useAuthStore';
import { useMemberStore } from '@/stores/useMemberStore';
import { BugRow } from './BugRow';
import { BugDetailDrawer } from './BugDetailDrawer';
import { Pagination, usePagination } from '@/components/Pagination';
@@ -13,6 +14,7 @@ import { SearchInput, matchTitleOrNo } from '@/components/SearchInput';
import { useDebouncedValue } from '@/hooks/useDebouncedValue';
import { BUG_STATUS_LABEL, BUG_SEVERITY_LABEL, bugIntervals } from '@/lib/bug';
import { formatWorkHours, calcTwoMetrics } from '@/lib/work-hours';
import { isMemberReference, resolveMemberDisplayName } from '@/lib/member-system';
import type { BugStatus, BugSeverity } from '@/lib/bug';
interface Props {
@@ -25,6 +27,7 @@ export function BugTab({ versionId, requirementIds }: Props) {
const { testCases, fetchTestCases } = useTestCaseStore();
const { requirements } = useRequirementStore();
const user = useAuthStore((s) => s.user);
const members = useMemberStore((s) => s.members);
useEffect(() => { fetchBugs(); }, [fetchBugs]);
useEffect(() => { fetchTestCases(); }, [fetchTestCases]);
@@ -48,26 +51,36 @@ export function BugTab({ versionId, requirementIds }: Props) {
const [keyword, setKeyword] = useState('');
const debouncedKeyword = useDebouncedValue(keyword, 300);
const [selectedBugId, setSelectedBugId] = useState<string | null>(null);
const testCaseMap = useMemo(() => new Map(testCases.map((c) => [c.id, c])), [testCases]);
const currentMember = useMemo(() => members.find((m) => m.id === user?.id), [members, user?.id]);
const currentUserName = currentMember?.name ?? user?.name;
const assignees = useMemo(() => {
const names = new Set(versionBugs.map((b) => resolveMemberDisplayName(b.assigneeId, members)));
return Array.from(names);
}, [versionBugs, members]);
const filteredBugs = useMemo(() => {
let result = versionBugs;
if (filterAssignee) result = result.filter((b) => b.assigneeId === filterAssignee);
if (filterAssignee === '__me') {
if (currentMember) {
result = result.filter((b) => isMemberReference(b.assigneeId, currentMember));
} else {
result = [];
}
} else if (filterAssignee) {
result = result.filter((b) => resolveMemberDisplayName(b.assigneeId, members) === filterAssignee);
}
if (filterStatus) result = result.filter((b) => b.status === filterStatus);
if (filterSeverity) result = result.filter((b) => b.severity === filterSeverity);
if (debouncedKeyword) result = result.filter((b) => matchTitleOrNo({ title: b.title, no: b.bugNo }, debouncedKeyword));
return result;
}, [versionBugs, filterAssignee, filterStatus, filterSeverity, debouncedKeyword]);
}, [versionBugs, filterAssignee, filterStatus, filterSeverity, debouncedKeyword, currentMember, members]);
const { paged, page, setPage, total, pageSize, setPageSize } = usePagination(filteredBugs, 20);
const [selectedBugId, setSelectedBugId] = useState<string | null>(null);
const testCaseMap = useMemo(() => new Map(testCases.map((c) => [c.id, c])), [testCases]);
const assignees = useMemo(() => {
const names = new Set(versionBugs.map((b) => b.assigneeId));
return Array.from(names);
}, [versionBugs]);
const hasFilter = !!(filterAssignee || filterStatus || filterSeverity || debouncedKeyword);
return (
@@ -94,8 +107,8 @@ export function BugTab({ versionId, requirementIds }: Props) {
<SearchInput value={keyword} onChange={(v) => { setKeyword(v); setPage(1); }} placeholder="搜索标题/编号" />
<select value={filterAssignee} onChange={(e) => { setFilterAssignee(e.target.value); setPage(1); }} className="h-7 rounded-md border border-[var(--line)] bg-[var(--bg-card)] px-2 text-[11px] text-[var(--ink-soft)] focus:border-[var(--accent)] focus:outline-none">
<option value=""></option>
{user?.name && <option value={user.name}>Bug</option>}
{assignees.filter((a) => a !== user?.name).map((a) => <option key={a} value={a}>{a}</option>)}
{currentMember && <option value="__me">Bug</option>}
{assignees.filter((a) => a !== currentUserName).map((a) => <option key={a} value={a}>{a}</option>)}
</select>
<select value={filterStatus} onChange={(e) => { setFilterStatus(e.target.value); setPage(1); }} className="h-7 rounded-md border border-[var(--line)] bg-[var(--bg-card)] px-2 text-[11px] text-[var(--ink-soft)] focus:border-[var(--accent)] focus:outline-none">
<option value=""></option>