diff --git a/src/pages/index.vue b/src/pages/index.vue index f6c99f7..8e38fb4 100644 --- a/src/pages/index.vue +++ b/src/pages/index.vue @@ -183,52 +183,217 @@ {{ state.snackbarText }} - + - 编辑出勤状态 + + + 出勤状态管理 + + + {{ state.dateString }} + + - - - 批量操作 - - - - 全部到齐 - - - 全部请假 - - - 全部迟到 - - - - - + - - 学生列表 - - - - + + + + + +
支持筛选姓氏,如输入"张"可筛选所有姓张的学生
+ + +
+ + {{ surname.name }} + ({{ surname.count }}) + +
+ + +
+
搜索建议:
+
+ + {{ suggestion }} + +
+
+
+
+ + +
+
+ + 到课 + + + + 请假 + + + 迟到 + + + 不参与 + +
+
+ + + + + + +
+
+ + {{ getStudentStatusIcon(state.studentList.indexOf(student)) }} + +
{{ student }}
+
+
+
+ + + + +
+
+
+
+
+ + + +
批量操作
+ + + 全部到齐 + + + 全部请假 + + + + 全部迟到 + + + 全部不参与 + + +
+
+
+ + - 保存 + + 取消 + + + mdi-content-save + 保存 +
@@ -348,6 +513,9 @@ export default { resolve: null, reject: null, }, + attendanceSearch: "", + attendanceFilter: [], + searchSuggestions: [], }; }, @@ -471,6 +639,56 @@ export default { showAntiScreenBurnCard() { return getSetting("display.showAntiScreenBurnCard"); }, + filteredStudents() { + let students = [...this.state.studentList]; + + // 应用搜索过滤 + if (this.attendanceSearch) { + const searchTerm = this.attendanceSearch.toLowerCase(); + students = students.filter(student => + student.toLowerCase().includes(searchTerm) + ); + } + + // 应用状态过滤 + if (this.attendanceFilter && this.attendanceFilter.length > 0) { + students = students.filter(student => { + const index = this.state.studentList.indexOf(student); + if (this.attendanceFilter.includes('present') && this.isPresent(index)) return true; + if (this.attendanceFilter.includes('absent') && this.isAbsent(index)) return true; + if (this.attendanceFilter.includes('late') && this.isLate(index)) return true; + if (this.attendanceFilter.includes('exclude') && this.isExclude(index)) return true; + return false; + }); + } + + return students; + }, + extractedSurnames() { + // 从学生名单中提取姓氏并统计数量 + if (!this.state.studentList || this.state.studentList.length === 0) { + return []; + } + + const surnameMap = new Map(); + + this.state.studentList.forEach(student => { + if (student && student.length > 0) { + // 中文姓名通常姓在前,取第一个字作为姓氏 + const surname = student.charAt(0); + if (surnameMap.has(surname)) { + surnameMap.set(surname, surnameMap.get(surname) + 1); + } else { + surnameMap.set(surname, 1); + } + } + }); + + // 转换为数组并按数量排序 + return Array.from(surnameMap.entries()) + .map(([name, count]) => ({ name, count })) + .sort((a, b) => b.count - a.count); + }, }, watch: { @@ -977,6 +1195,12 @@ export default { this.state.boardData.attendance.exclude = []; this.state.synced = false; }, + setAllExclude() { + this.state.boardData.attendance.absent = []; + this.state.boardData.attendance.late = []; + this.state.boardData.attendance.exclude = [...this.state.studentList]; + this.state.synced = false; + }, isPresent(index) { const student = this.state.studentList[index]; @@ -1008,14 +1232,14 @@ export default { setPresent(index) { const student = this.state.studentList[index]; - const { absent, late, exclude } = this.state.boardData.attendance; - this.state.boardData.attendance.absent = absent.filter( + // 从所有状态列表中移除该学生 + this.state.boardData.attendance.absent = this.state.boardData.attendance.absent.filter( (name) => name !== student ); - this.state.boardData.attendance.late = late.filter( + this.state.boardData.attendance.late = this.state.boardData.attendance.late.filter( (name) => name !== student ); - this.state.boardData.attendance.exclude = exclude.filter( + this.state.boardData.attendance.exclude = this.state.boardData.attendance.exclude.filter( (name) => name !== student ); this.state.synced = false; @@ -1023,29 +1247,29 @@ export default { setAbsent(index) { const student = this.state.studentList[index]; - if (!this.state.boardData.attendance.absent.includes(student)) { - this.setPresent(index); - this.state.boardData.attendance.absent.push(student); - this.state.synced = false; - } + // 先从所有状态列表中移除该学生 + this.setPresent(index); + // 然后添加到请假列表 + this.state.boardData.attendance.absent.push(student); + this.state.synced = false; }, setLate(index) { const student = this.state.studentList[index]; - if (!this.state.boardData.attendance.late.includes(student)) { - this.setPresent(index); - this.state.boardData.attendance.late.push(student); - this.state.synced = false; - } + // 先从所有状态列表中移除该学生 + this.setPresent(index); + // 然后添加到迟到列表 + this.state.boardData.attendance.late.push(student); + this.state.synced = false; }, setExclude(index) { const student = this.state.studentList[index]; - if (!this.state.boardData.attendance.exclude.includes(student)) { - this.setPresent(index); - this.state.boardData.attendance.exclude.push(student); - this.state.synced = false; - } + // 先从所有状态列表中移除该学生 + this.setPresent(index); + // 然后添加到不参与列表 + this.state.boardData.attendance.exclude.push(student); + this.state.synced = false; }, async saveAttendance() { @@ -1175,6 +1399,93 @@ export default { document.msFullscreenElement ); }, + + getStudentStatusColor(index) { + const studentName = this.state.studentList[index]; + if (this.state.boardData.attendance.absent.includes(studentName)) return 'error'; + if (this.state.boardData.attendance.late.includes(studentName)) return 'warning'; + if (this.state.boardData.attendance.exclude.includes(studentName)) return 'grey'; + return 'success'; + }, + + getStudentStatusVariant(index) { + const studentName = this.state.studentList[index]; + if (this.state.boardData.attendance.absent.includes(studentName) || + this.state.boardData.attendance.late.includes(studentName) || + this.state.boardData.attendance.exclude.includes(studentName)) { + return 'tonal'; + } + return 'outlined'; + }, + + getStudentStatusIcon(index) { + const studentName = this.state.studentList[index]; + if (this.state.boardData.attendance.absent.includes(studentName)) return 'mdi-account-off'; + if (this.state.boardData.attendance.late.includes(studentName)) return 'mdi-clock-alert'; + if (this.state.boardData.attendance.exclude.includes(studentName)) return 'mdi-account-cancel'; + return 'mdi-account-check'; + }, + + getStudentStatusText(index) { + const studentName = this.state.studentList[index]; + if (this.state.boardData.attendance.absent.includes(studentName)) return '请假'; + if (this.state.boardData.attendance.late.includes(studentName)) return '迟到'; + if (this.state.boardData.attendance.exclude.includes(studentName)) return '不参与'; + return '到课'; + }, + + handleSearchChange(value) { + // 生成搜索建议 + this.generateSearchSuggestions(value); + }, + + generateSearchSuggestions(searchTerm) { + if (!searchTerm || searchTerm.length < 1) { + this.searchSuggestions = []; + return; + } + + // 查找匹配的学生名字 + const matchingStudents = this.state.studentList.filter(student => + student.toLowerCase().includes(searchTerm.toLowerCase()) + ); + + // 如果有精确匹配,不显示建议 + if (matchingStudents.some(student => student === searchTerm)) { + this.searchSuggestions = []; + return; + } + + // 提取匹配的部分作为建议 + const suggestions = new Set(); + + // 如果是单个字符,可能是在搜索姓氏 + if (searchTerm.length === 1) { + // 添加所有以该字符开头的学生的完整姓名作为建议 + matchingStudents.forEach(student => { + if (student.startsWith(searchTerm)) { + suggestions.add(student); + } + }); + } else { + // 对于多字符搜索,添加包含该字符串的学生名字 + matchingStudents.forEach(student => { + suggestions.add(student); + }); + } + + // 限制建议数量 + this.searchSuggestions = Array.from(suggestions).slice(0, 5); + }, + + toggleFilter(filter) { + const index = this.attendanceFilter.indexOf(filter); + if (index === -1) { + this.attendanceFilter.push(filter); + } else { + this.attendanceFilter.splice(index, 1); + } + }, }, }; @@ -1251,4 +1562,60 @@ export default { box-shadow: 0 0 15px rgba(33, 150, 243, 0.3); } } + +// 出勤管理对话框样式 +.attendance-stat { + height: 100%; +} + +// 姓氏筛选按钮 +.surname-btn { + margin: 2px; + min-width: 0; + padding: 0 8px; + + &:active { + transform: scale(0.95); + } +} + +// 搜索建议按钮 +.suggestion-btn { + margin: 2px; + min-width: 0; + padding: 0 6px; + + &:active { + transform: scale(0.95); + } +} + +// 适配触摸屏 +@media (hover: none) { + .student-card .attendance-actions { + opacity: 1; + } +} + +// 小屏幕适配 +@media (max-width: 600px) { + .student-card { + .attendance-actions .v-btn { + margin: 0 1px; + min-width: 28px; + width: 28px; + height: 28px; + } + } +} + +// 过滤器芯片 +.filter-chip { + cursor: pointer; + margin: 2px; + + &:active { + transform: scale(0.95); + } +}