diff --git a/src/components/Home.tsx b/src/components/Home.tsx index 781792c..62c56e7 100644 --- a/src/components/Home.tsx +++ b/src/components/Home.tsx @@ -24,6 +24,7 @@ interface reason { } type SortType = "alphabet" | "surname" | "score" +type LayoutType = "grouped" | "squareGrid" type SearchKeyboardLayout = "t9" | "qwerty26" const T9_KEY_MAP: Record = { @@ -66,6 +67,7 @@ export const Home: React.FC = ({ canEdit, isPortraitMode = false }) = const [reasons, setReasons] = useState([]) const [loading, setLoading] = useState(false) const [sortType, setSortType] = useState("alphabet") + const [layoutType, setLayoutType] = useState("grouped") const [searchKeyword, setSearchKeyword] = useState("") const [showPinyinKeyboard, setShowPinyinKeyboard] = useState(false) const [searchKeyboardLayout, setSearchKeyboardLayout] = useState("qwerty26") @@ -327,6 +329,10 @@ export const Home: React.FC = ({ canEdit, isPortraitMode = false }) = }, [students, searchKeyword, sortType, matchStudentName]) const groupedStudents = useMemo(() => { + if (layoutType === "squareGrid") { + return [{ key: "all", students: sortedStudents }] + } + if (sortType === "score" || (sortType === "alphabet" && searchKeyword)) { return [{ key: "all", students: sortedStudents }] } @@ -341,7 +347,7 @@ export const Home: React.FC = ({ canEdit, isPortraitMode = false }) = return Object.entries(groups) .sort(([a], [b]) => a.localeCompare(b, "zh-CN")) .map(([key, students]) => ({ key, students })) - }, [sortedStudents, sortType, searchKeyword]) + }, [sortedStudents, sortType, searchKeyword, layoutType]) const groupedReasons = useMemo(() => { const groups: Record = {} @@ -820,7 +826,190 @@ export const Home: React.FC = ({ canEdit, isPortraitMode = false }) = ) } + const renderStudentSquareCard = (student: student, index: number) => { + const avatarText = getDisplayText(student.name) + const avatarColor = getAvatarColor(student.name) + const isQuickActionMode = quickActionStudentId === student.id + + let rankBadge: string | null = null + if (sortType === "score" && !searchKeyword) { + if (index === 0) rankBadge = "🥇" + else if (index === 1) rankBadge = "🥈" + else if (index === 2) rankBadge = "🥉" + } + + return ( +
{ + if (suppressClickRef.current) { + suppressClickRef.current = false + e.preventDefault() + e.stopPropagation() + return + } + openOperation(student) + }} + onMouseDown={(e) => { + if (e.button !== 0) return + startLongPress(student) + }} + onMouseUp={cancelLongPress} + onMouseLeave={cancelLongPress} + onTouchStart={() => startLongPress(student)} + onTouchEnd={cancelLongPress} + onTouchCancel={cancelLongPress} + onContextMenu={(e) => { + e.preventDefault() + openQuickAction(student) + }} + style={{ + cursor: "pointer", + position: "relative", + aspectRatio: "1 / 1", + }} + > + + {rankBadge && ( +
+ {rankBadge} +
+ )} +
+ {student.avatarUrl ? ( + {student.name} + ) : ( +
1 ? "16px" : "20px", + flexShrink: 0, + boxShadow: `0 4px 10px ${avatarColor}40`, + }} + > + {avatarText} +
+ )} +
+ {student.name} +
+ {isQuickActionMode ? ( + + + + + ) : ( + 0 ? "success" : student.score < 0 ? "error" : "default"} + style={{ fontWeight: "bold", marginInlineEnd: 0 }} + > + {student.score > 0 ? `+${student.score}` : student.score} + + )} +
+
+
+ ) + } + const renderGroupedCards = () => { + if (layoutType === "squareGrid") { + return ( +
+ {sortedStudents.map((student, idx) => renderStudentSquareCard(student, idx))} +
+ ) + } + return groupedStudents.map((group) => (
= ({ canEdit, isPortraitMode = false }) = const shouldShowQuickNav = groupedStudents.length > 1 && + layoutType !== "squareGrid" && sortType !== "score" && !(sortType === "alphabet" && searchKeyword) @@ -1616,6 +1806,15 @@ export const Home: React.FC = ({ canEdit, isPortraitMode = false }) = { value: "score", label: t("home.sortBy.score") }, ]} /> +