mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-21 09:39:03 +08:00
优化右侧姓氏索引栏超高时的自适应缩放
This commit is contained in:
+68
-19
@@ -517,10 +517,54 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
|||||||
const isNavDragging = useRef(false)
|
const isNavDragging = useRef(false)
|
||||||
const bodyUserSelectRef = useRef("")
|
const bodyUserSelectRef = useRef("")
|
||||||
const bodyWebkitUserSelectRef = useRef("")
|
const bodyWebkitUserSelectRef = useRef("")
|
||||||
|
const [viewportHeight, setViewportHeight] = useState(() =>
|
||||||
|
typeof window === "undefined" ? 900 : window.innerHeight
|
||||||
|
)
|
||||||
const [navActiveKey, setNavActiveKey] = useState<string | null>(null)
|
const [navActiveKey, setNavActiveKey] = useState<string | null>(null)
|
||||||
const [navIndicatorY, setNavIndicatorY] = useState(0)
|
const [navIndicatorY, setNavIndicatorY] = useState(0)
|
||||||
const [isNavDraggingState, setIsNavDraggingState] = useState(false)
|
const [isNavDraggingState, setIsNavDraggingState] = useState(false)
|
||||||
|
|
||||||
|
const quickNavLayout = useMemo(() => {
|
||||||
|
const baseItemSize = 24
|
||||||
|
const baseFontSize = 11
|
||||||
|
const basePaddingY = 8
|
||||||
|
const basePaddingX = 4
|
||||||
|
const baseBorderRadius = 20
|
||||||
|
const baseBubbleSize = 34
|
||||||
|
const baseBubbleFontSize = 13
|
||||||
|
const minScale = 0.58
|
||||||
|
const maxHeight = Math.max(120, Math.floor(viewportHeight * 0.8))
|
||||||
|
const count = groupedStudents.length
|
||||||
|
|
||||||
|
if (count <= 0) {
|
||||||
|
return {
|
||||||
|
maxHeight,
|
||||||
|
itemSize: baseItemSize,
|
||||||
|
fontSize: baseFontSize,
|
||||||
|
paddingY: basePaddingY,
|
||||||
|
paddingX: basePaddingX,
|
||||||
|
borderRadius: baseBorderRadius,
|
||||||
|
bubbleSize: baseBubbleSize,
|
||||||
|
bubbleFontSize: baseBubbleFontSize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseContentHeight = count * baseItemSize + basePaddingY * 2
|
||||||
|
const rawScale = Math.min(1, maxHeight / baseContentHeight)
|
||||||
|
const scale = Math.max(minScale, rawScale)
|
||||||
|
|
||||||
|
return {
|
||||||
|
maxHeight,
|
||||||
|
itemSize: Math.max(14, baseItemSize * scale),
|
||||||
|
fontSize: Math.max(8, baseFontSize * scale),
|
||||||
|
paddingY: Math.max(4, basePaddingY * scale),
|
||||||
|
paddingX: Math.max(3, basePaddingX * scale),
|
||||||
|
borderRadius: Math.max(12, baseBorderRadius * scale),
|
||||||
|
bubbleSize: Math.max(22, baseBubbleSize * scale),
|
||||||
|
bubbleFontSize: Math.max(10, baseBubbleFontSize * scale),
|
||||||
|
}
|
||||||
|
}, [groupedStudents.length, viewportHeight])
|
||||||
|
|
||||||
const setNavDraggingState = useCallback((dragging: boolean) => {
|
const setNavDraggingState = useCallback((dragging: boolean) => {
|
||||||
isNavDragging.current = dragging
|
isNavDragging.current = dragging
|
||||||
setIsNavDraggingState(dragging)
|
setIsNavDraggingState(dragging)
|
||||||
@@ -541,22 +585,23 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
|||||||
if (!navContainerRef.current) return
|
if (!navContainerRef.current) return
|
||||||
const rect = navContainerRef.current.getBoundingClientRect()
|
const rect = navContainerRef.current.getBoundingClientRect()
|
||||||
const y = clientY - rect.top
|
const y = clientY - rect.top
|
||||||
const items = navContainerRef.current.children
|
const itemCount = groupedStudents.length
|
||||||
const itemCount = items.length
|
|
||||||
if (itemCount === 0) return
|
if (itemCount === 0) return
|
||||||
|
|
||||||
const itemHeight = rect.height / itemCount
|
const innerTop = quickNavLayout.paddingY
|
||||||
const index = Math.floor(y / itemHeight)
|
const innerBottom = rect.height - quickNavLayout.paddingY
|
||||||
|
const clampedY = Math.max(innerTop, Math.min(innerBottom - 0.001, y))
|
||||||
|
const index = Math.floor((clampedY - innerTop) / quickNavLayout.itemSize)
|
||||||
const safeIndex = Math.max(0, Math.min(itemCount - 1, index))
|
const safeIndex = Math.max(0, Math.min(itemCount - 1, index))
|
||||||
|
|
||||||
const targetGroup = groupedStudents[safeIndex]
|
const targetGroup = groupedStudents[safeIndex]
|
||||||
if (targetGroup) {
|
if (targetGroup) {
|
||||||
setNavActiveKey(targetGroup.key)
|
setNavActiveKey(targetGroup.key)
|
||||||
setNavIndicatorY((safeIndex + 0.5) * itemHeight)
|
setNavIndicatorY(quickNavLayout.paddingY + (safeIndex + 0.5) * quickNavLayout.itemSize)
|
||||||
scrollToGroup(targetGroup.key)
|
scrollToGroup(targetGroup.key)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[groupedStudents]
|
[groupedStudents, quickNavLayout.itemSize, quickNavLayout.paddingY]
|
||||||
)
|
)
|
||||||
|
|
||||||
const onNavMouseDown = (e: React.MouseEvent) => {
|
const onNavMouseDown = (e: React.MouseEvent) => {
|
||||||
@@ -599,6 +644,12 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
|||||||
setNavDraggingState(false)
|
setNavDraggingState(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const onResize = () => setViewportHeight(window.innerHeight)
|
||||||
|
window.addEventListener("resize", onResize)
|
||||||
|
return () => window.removeEventListener("resize", onResize)
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("mousemove", onGlobalMouseMove)
|
document.removeEventListener("mousemove", onGlobalMouseMove)
|
||||||
@@ -631,9 +682,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
|||||||
setNavActiveKey(currentKey)
|
setNavActiveKey(currentKey)
|
||||||
const idx = groupedStudents.findIndex((g) => g.key === currentKey)
|
const idx = groupedStudents.findIndex((g) => g.key === currentKey)
|
||||||
if (idx >= 0 && navContainerRef.current) {
|
if (idx >= 0 && navContainerRef.current) {
|
||||||
const rect = navContainerRef.current.getBoundingClientRect()
|
setNavIndicatorY(quickNavLayout.paddingY + (idx + 0.5) * quickNavLayout.itemSize)
|
||||||
const itemHeight = rect.height / groupedStudents.length
|
|
||||||
setNavIndicatorY((idx + 0.5) * itemHeight)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -645,7 +694,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
|||||||
window.removeEventListener("scroll", refreshActiveByScroll)
|
window.removeEventListener("scroll", refreshActiveByScroll)
|
||||||
window.removeEventListener("resize", refreshActiveByScroll)
|
window.removeEventListener("resize", refreshActiveByScroll)
|
||||||
}
|
}
|
||||||
}, [groupedStudents])
|
}, [groupedStudents, quickNavLayout.itemSize, quickNavLayout.paddingY])
|
||||||
|
|
||||||
const renderQuickNav = () => {
|
const renderQuickNav = () => {
|
||||||
if (
|
if (
|
||||||
@@ -671,11 +720,11 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
backgroundColor: "var(--ss-card-bg)",
|
backgroundColor: "var(--ss-card-bg)",
|
||||||
padding: "8px 4px",
|
padding: `${quickNavLayout.paddingY}px ${quickNavLayout.paddingX}px`,
|
||||||
borderRadius: "20px",
|
borderRadius: `${quickNavLayout.borderRadius}px`,
|
||||||
boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
|
boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
|
||||||
zIndex: 100,
|
zIndex: 100,
|
||||||
maxHeight: "80vh",
|
maxHeight: `${quickNavLayout.maxHeight}px`,
|
||||||
border: "1px solid var(--ss-border-color)",
|
border: "1px solid var(--ss-border-color)",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
@@ -688,8 +737,8 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
|||||||
position: "absolute",
|
position: "absolute",
|
||||||
left: "-10px",
|
left: "-10px",
|
||||||
top: `${navIndicatorY}px`,
|
top: `${navIndicatorY}px`,
|
||||||
width: "34px",
|
width: `${quickNavLayout.bubbleSize}px`,
|
||||||
height: "34px",
|
height: `${quickNavLayout.bubbleSize}px`,
|
||||||
transform: "translate(-100%, -50%)",
|
transform: "translate(-100%, -50%)",
|
||||||
borderRadius: "50%",
|
borderRadius: "50%",
|
||||||
backgroundColor: isNavDraggingState
|
backgroundColor: isNavDraggingState
|
||||||
@@ -726,7 +775,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
|||||||
<span
|
<span
|
||||||
style={{
|
style={{
|
||||||
color: isNavDraggingState ? "#fff" : "var(--ant-color-primary, #1890ff)",
|
color: isNavDraggingState ? "#fff" : "var(--ant-color-primary, #1890ff)",
|
||||||
fontSize: "13px",
|
fontSize: `${quickNavLayout.bubbleFontSize}px`,
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
lineHeight: 1,
|
lineHeight: 1,
|
||||||
}}
|
}}
|
||||||
@@ -739,12 +788,12 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
|
|||||||
<div
|
<div
|
||||||
key={group.key}
|
key={group.key}
|
||||||
style={{
|
style={{
|
||||||
width: "24px",
|
width: `${quickNavLayout.itemSize}px`,
|
||||||
height: "24px",
|
height: `${quickNavLayout.itemSize}px`,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
fontSize: "11px",
|
fontSize: `${quickNavLayout.fontSize}px`,
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
color:
|
color:
|
||||||
navActiveKey === group.key
|
navActiveKey === group.key
|
||||||
|
|||||||
Reference in New Issue
Block a user