From 8f79653e06787fb4eecdfa534333deab61bf75db Mon Sep 17 00:00:00 2001
From: Linkon <116425752+Linkon-lcw@users.noreply.github.com>
Date: Sun, 18 Jan 2026 23:26:34 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E4=BE=A7=E8=BE=B9?=
=?UTF-8?q?=E6=A0=8F=E5=8A=A8=E7=94=BB=E5=BB=B6=E8=BF=9F=E5=92=8C=E6=80=A7?=
=?UTF-8?q?=E8=83=BD=EF=BC=8C=E5=AE=9E=E7=8E=B0=E8=B7=AF=E7=94=B1=E6=87=92?=
=?UTF-8?q?=E5=8A=A0=E8=BD=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
调整侧边栏动画延迟时间以匹配透明度动画,添加contain: paint优化性能
将路由组件改为懒加载模式并添加加载状态
优化学生搜索功能,预计算拼音数据提升性能
---
src/renderer/src/assets/main.css | 5 +-
src/renderer/src/components/ContentArea.tsx | 65 +++++++++-----
src/renderer/src/components/GlobalSidebar.tsx | 4 +-
src/renderer/src/components/Home.tsx | 88 ++++++++++---------
4 files changed, 93 insertions(+), 69 deletions(-)
diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css
index 28e027f..37f2d7e 100644
--- a/src/renderer/src/assets/main.css
+++ b/src/renderer/src/assets/main.css
@@ -114,7 +114,7 @@ html[theme-mode='dark'] .ss-sidebar .t-menu__item.t-is-active svg:not([fill='non
z-index: 10;
flex-shrink: 0;
animation: twinkle-animation 3s infinite ease-in-out;
- transition: opacity 0.1s ease, transform 0.1s ease;
+ transition: opacity 0.07s ease, transform 0.15s ease;
}
.global-sidebar-toggle.hidden {
@@ -134,9 +134,10 @@ html[theme-mode='dark'] .ss-sidebar .t-menu__item.t-is-active svg:not([fill='non
padding: 12px 8px;
gap: 12px;
width: 60px;
- transition: opacity 0.15s ease, transform 0.15s cubic-bezier(0.34, 1.56, 0.64, 1);
+ transition: opacity 0.1s ease, transform 0.1s cubic-bezier(0.34, 1.56, 0.64, 1);
backface-visibility: hidden;
transform: translateZ(0);
+ contain: paint; /* 性能优化:限制重绘范围 */
}
.sidebar-content-area.hidden {
diff --git a/src/renderer/src/components/ContentArea.tsx b/src/renderer/src/components/ContentArea.tsx
index 11da34a..a811a6d 100644
--- a/src/renderer/src/components/ContentArea.tsx
+++ b/src/renderer/src/components/ContentArea.tsx
@@ -1,14 +1,16 @@
-import { Layout, Space, Button, Tag } from 'tdesign-react'
+import React, { Suspense, lazy } from 'react'
+import { Layout, Space, Button, Tag, Loading } from 'tdesign-react'
import { Routes, Route, Navigate } from 'react-router-dom'
-import { Home } from './Home'
-import { StudentManager } from './StudentManager'
-import { Settings } from './Settings'
-import { ReasonManager } from './ReasonManager'
-import { ScoreManager } from './ScoreManager'
-import { Leaderboard } from './Leaderboard'
-import { SettlementHistory } from './SettlementHistory'
import { WindowControls } from './WindowControls'
+const Home = lazy(() => import('./Home').then(m => ({ default: m.Home })))
+const StudentManager = lazy(() => import('./StudentManager').then(m => ({ default: m.StudentManager })))
+const Settings = lazy(() => import('./Settings').then(m => ({ default: m.Settings })))
+const ReasonManager = lazy(() => import('./ReasonManager').then(m => ({ default: m.ReasonManager })))
+const ScoreManager = lazy(() => import('./ScoreManager').then(m => ({ default: m.ScoreManager })))
+const Leaderboard = lazy(() => import('./Leaderboard').then(m => ({ default: m.Leaderboard })))
+const SettlementHistory = lazy(() => import('./SettlementHistory').then(m => ({ default: m.SettlementHistory })))
+
const { Content } = Layout
interface ContentAreaProps {
@@ -85,22 +87,37 @@ export function ContentArea({
-
- }
- />
- } />
- }
- />
- } />
- } />
- } />
- } />
- } />
-
+
+
+
+ }
+ >
+
+ }
+ />
+ } />
+ }
+ />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
)
diff --git a/src/renderer/src/components/GlobalSidebar.tsx b/src/renderer/src/components/GlobalSidebar.tsx
index ee978f3..d7bc00d 100644
--- a/src/renderer/src/components/GlobalSidebar.tsx
+++ b/src/renderer/src/components/GlobalSidebar.tsx
@@ -38,10 +38,10 @@ export const GlobalSidebar: React.FC = () => {
if ((window as any).api) {
(window as any).api.windowResize(24, 300)
}
- // 3. 最后重新显示三角
+ // 3. 最后重新显示三角(等待透明度动画完成)
setTimeout(() => {
setShowToggle(true)
- }, 20)
+ }, 150)
}, 150)
}
diff --git a/src/renderer/src/components/Home.tsx b/src/renderer/src/components/Home.tsx
index 494e027..d274829 100644
--- a/src/renderer/src/components/Home.tsx
+++ b/src/renderer/src/components/Home.tsx
@@ -18,6 +18,8 @@ interface student {
id: number
name: string
score: number
+ pinyinName?: string
+ pinyinFirst?: string
}
interface reason {
@@ -51,33 +53,6 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
window.dispatchEvent(new CustomEvent('ss:data-updated', { detail: { category } }))
}
- const fetchData = useCallback(async (silent = false) => {
- if (!(window as any).api) return
- if (!silent) setLoading(true)
- const [stuRes, reaRes] = await Promise.all([
- (window as any).api.queryStudents({}),
- (window as any).api.queryReasons()
- ])
-
- if (stuRes.success) setStudents(stuRes.data)
- if (reaRes.success) setReasons(reaRes.data)
- if (!silent) setLoading(false)
- }, [])
-
- useEffect(() => {
- fetchData()
- const onDataUpdated = (e: any) => {
- const category = e?.detail?.category
- // 仅在学生名单、理由列表或全量更新时才重新拉取数据
- // 积分变动(events)现在由本地状态维护,不再触发全量刷新以防止页面跳动
- if (category === 'students' || category === 'reasons' || category === 'all') {
- fetchData(true)
- }
- }
- window.addEventListener('ss:data-updated', onDataUpdated as any)
- return () => window.removeEventListener('ss:data-updated', onDataUpdated as any)
- }, [fetchData])
-
// 获取姓氏
const getSurname = (name: string) => {
if (!name) return ''
@@ -95,6 +70,38 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
return py ? py.toUpperCase() : '#'
}
+ const fetchData = useCallback(async (silent = false) => {
+ if (!(window as any).api) return
+ if (!silent) setLoading(true)
+ const [stuRes, reaRes] = await Promise.all([
+ (window as any).api.queryStudents({}),
+ (window as any).api.queryReasons()
+ ])
+
+ if (stuRes.success) {
+ const enrichedStudents = (stuRes.data as student[]).map(s => ({
+ ...s,
+ pinyinName: pinyin(s.name, { toneType: 'none' }).toLowerCase(),
+ pinyinFirst: getFirstLetter(s.name)
+ }))
+ setStudents(enrichedStudents)
+ }
+ if (reaRes.success) setReasons(reaRes.data)
+ if (!silent) setLoading(false)
+ }, [])
+
+ useEffect(() => {
+ fetchData()
+ const onDataUpdated = (e: any) => {
+ const category = e?.detail?.category
+ if (category === 'students' || category === 'reasons' || category === 'all') {
+ fetchData(true)
+ }
+ }
+ window.addEventListener('ss:data-updated', onDataUpdated as any)
+ return () => window.removeEventListener('ss:data-updated', onDataUpdated as any)
+ }, [fetchData])
+
// 获取展示用的文字
const getDisplayText = (name: string) => {
if (!name) return ''
@@ -102,23 +109,22 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
}
// 拼音匹配
- const matchStudentName = useCallback((name: string, keyword: string) => {
+ const matchStudentName = useCallback((s: student, keyword: string) => {
const q0 = keyword.trim().toLowerCase()
if (!q0) return true
- const nameLower = String(name).toLowerCase()
+ const nameLower = String(s.name).toLowerCase()
if (nameLower.includes(q0)) return true
+ const pyLower = s.pinyinName || ''
+ if (pyLower.includes(q0)) return true
+
const q1 = q0.replace(/\s+/g, '')
- if (q1 && nameLower.replace(/\s+/g, '').includes(q1)) return true
+ if (q1 && (nameLower.replace(/\s+/g, '').includes(q1) || pyLower.replace(/\s+/g, '').includes(q1))) return true
try {
- const m0 = match(name, q0)
+ const m0 = match(s.name, q0)
if (Array.isArray(m0)) return true
- if (q1 && q1 !== q0) {
- const m1 = match(name, q1)
- if (Array.isArray(m1)) return true
- }
} catch {
return false
}
@@ -128,13 +134,13 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
// 过滤和排序学生
const sortedStudents = useMemo(() => {
- const filtered = students.filter((s) => matchStudentName(s.name, searchKeyword))
+ const filtered = students.filter((s) => matchStudentName(s, searchKeyword))
switch (sortType) {
case 'alphabet':
return filtered.sort((a, b) => {
- const pyA = pinyin(a.name, { toneType: 'none' })
- const pyB = pinyin(b.name, { toneType: 'none' })
+ const pyA = a.pinyinName || ''
+ const pyB = b.pinyinName || ''
return pyA.localeCompare(pyB)
})
case 'surname':
@@ -161,7 +167,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
const groups: Record = {}
sortedStudents.forEach((s) => {
- const key = sortType === 'alphabet' ? getFirstLetter(s.name) : getSurname(s.name)
+ const key = sortType === 'alphabet' ? (s.pinyinFirst || '#') : getSurname(s.name)
if (!groups[key]) groups[key] = []
groups[key].push(s)
})
@@ -822,7 +828,7 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
suffixIcon={
reasonContent ? (
setReasonContent('')}
+ onClick={() => setSearchKeyword('')}
style={{ cursor: 'pointer' }}
/>
) : undefined
@@ -883,4 +889,4 @@ export const Home: React.FC<{ canEdit: boolean }> = ({ canEdit }) => {
)
-}
+}
\ No newline at end of file