mirror of
https://github.com/ZeroCatDev/Classworks.git
synced 2025-07-04 18:39:22 +00:00
1
This commit is contained in:
parent
ebf3e9df94
commit
9bb3f06ba1
41
src/components/common/UnsavedWarning.vue
Normal file
41
src/components/common/UnsavedWarning.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<div class="warning-container">
|
||||||
|
<v-chip
|
||||||
|
v-if="show"
|
||||||
|
color="warning"
|
||||||
|
size="small"
|
||||||
|
class="warning-chip"
|
||||||
|
>
|
||||||
|
{{ message }}
|
||||||
|
</v-chip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'UnsavedWarning',
|
||||||
|
props: {
|
||||||
|
show: Boolean,
|
||||||
|
message: {
|
||||||
|
type: String,
|
||||||
|
default: '未保存'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.warning-container {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-chip {
|
||||||
|
animation: fade-in 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from { opacity: 0; transform: translateY(-10px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
</style>
|
@ -10,14 +10,10 @@
|
|||||||
</template>
|
</template>
|
||||||
<v-card-title class="text-h6">学生列表</v-card-title>
|
<v-card-title class="text-h6">学生列表</v-card-title>
|
||||||
<template #append>
|
<template #append>
|
||||||
<v-chip
|
<unsaved-warning
|
||||||
v-if="hasChanges"
|
:show="hasChanges"
|
||||||
color="warning"
|
message="有未保存的更改"
|
||||||
size="small"
|
/>
|
||||||
class="mr-2"
|
|
||||||
>
|
|
||||||
未保存
|
|
||||||
</v-chip>
|
|
||||||
<v-btn
|
<v-btn
|
||||||
:color="modelValue.advanced ? 'primary' : undefined"
|
:color="modelValue.advanced ? 'primary' : undefined"
|
||||||
variant="text"
|
variant="text"
|
||||||
@ -217,9 +213,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import UnsavedWarning from '../common/UnsavedWarning.vue'
|
||||||
|
import '@/styles/warnings.scss'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'StudentListCard',
|
name: 'StudentListCard',
|
||||||
|
components: {
|
||||||
|
UnsavedWarning
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -246,15 +247,12 @@ export default {
|
|||||||
index: -1,
|
index: -1,
|
||||||
name: ''
|
name: ''
|
||||||
},
|
},
|
||||||
savedState: { // 初始化为当前值而不是 null
|
savedState: null // 改为 null 初始值
|
||||||
list: [],
|
|
||||||
text: ''
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
// 移除这里的初始化,改为在 mounted 中处理
|
this.initializeSavedState()
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
@ -300,23 +298,17 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
hasChanges() {
|
hasChanges() {
|
||||||
const currentState = JSON.stringify({
|
return this.savedState && this.isStateChanged();
|
||||||
list: this.modelValue.list,
|
|
||||||
text: this.modelValue.text
|
|
||||||
});
|
|
||||||
const savedState = JSON.stringify(this.savedState);
|
|
||||||
// 检查 savedState 是否为初始状态
|
|
||||||
return this.savedState.list.length > 0 && currentState !== savedState;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
// 初始化方法
|
// 初始化方法
|
||||||
initializeSavedList() {
|
initializeSavedState() {
|
||||||
// 优先使用 modelValue 的当前值,否则使用 originalList
|
this.savedState = {
|
||||||
this.savedList = this.modelValue.list.length > 0
|
list: [...(this.modelValue.list.length ? this.modelValue.list : this.originalList)],
|
||||||
? [...this.modelValue.list]
|
text: this.modelValue.text || (this.originalList || []).join('\n')
|
||||||
: [...this.originalList];
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 列表状态检查
|
// 列表状态检查
|
||||||
@ -349,6 +341,7 @@ export default {
|
|||||||
list: [...this.modelValue.list],
|
list: [...this.modelValue.list],
|
||||||
text: this.modelValue.text
|
text: this.modelValue.text
|
||||||
};
|
};
|
||||||
|
this.$forceUpdate(); // 强制更新视图
|
||||||
},
|
},
|
||||||
|
|
||||||
// 学生管理方法
|
// 学生管理方法
|
||||||
@ -428,6 +421,7 @@ export default {
|
|||||||
this.updateSavedState();
|
this.updateSavedState();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存失败:', error);
|
console.error('保存失败:', error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -441,6 +435,18 @@ export default {
|
|||||||
text: value,
|
text: value,
|
||||||
list
|
list
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重写变更检测逻辑
|
||||||
|
isStateChanged() {
|
||||||
|
if (!this.savedState) return false;
|
||||||
|
|
||||||
|
const currentState = {
|
||||||
|
list: this.modelValue.list,
|
||||||
|
text: this.modelValue.text
|
||||||
|
};
|
||||||
|
|
||||||
|
return JSON.stringify(currentState) !== JSON.stringify(this.savedState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -456,16 +462,22 @@ export default {
|
|||||||
transition: opacity 0.2s ease;
|
transition: opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unsaved-changes {
|
/* 修改警告样式的选择器和实现 */
|
||||||
animation: pulse-warning 2s infinite; /* 更有意义的动画名称 */
|
.v-card.unsaved-changes {
|
||||||
border: 2px solid rgb(var(--v-theme-warning));
|
animation: pulse-warning 2s infinite;
|
||||||
|
border: 2px solid rgb(var(--v-theme-warning)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse-warning {
|
@keyframes pulse-warning {
|
||||||
0%, 100% { border-color: rgba(var(--v-theme-warning), 1); }
|
0%, 100% {
|
||||||
50% { border-color: rgba(var(--v-theme-warning), 0.5); }
|
border-color: rgba(var(--v-theme-warning), 1) !important;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
border-color: rgba(var(--v-theme-warning), 0.5) !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移动端样式 */
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.action-buttons {
|
.action-buttons {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
@click="$refs.messageLog.drawer = true"
|
@click="$refs.messageLog.drawer = true"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</v-app-bar>
|
</v-app-bar>{{ state.boardData }}
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<!-- 主要内容区域 -->
|
<!-- 主要内容区域 -->
|
||||||
<v-container class="main-window flex-grow-1 no-select" fluid>
|
<v-container class="main-window flex-grow-1 no-select" fluid>
|
||||||
@ -133,27 +133,27 @@
|
|||||||
@click="setAttendanceArea()"
|
@click="setAttendanceArea()"
|
||||||
>
|
>
|
||||||
<h1>出勤</h1>
|
<h1>出勤</h1>
|
||||||
<h2>应到: {{ state.studentList.length - state.excludeSet.size }}人</h2>
|
<h2>应到: {{ state.studentList.length - state.boardData.attendance.exclude.length }}人</h2>
|
||||||
<h2>
|
<h2>
|
||||||
实到:
|
实到:
|
||||||
{{
|
{{
|
||||||
state.studentList.length -
|
state.studentList.length -
|
||||||
state.selectedSet.size -
|
state.boardData.attendance.absent.length -
|
||||||
state.lateSet.size -
|
state.boardData.attendance.late.length -
|
||||||
state.excludeSet.size
|
state.boardData.attendance.exclude.length
|
||||||
}}人
|
}}人
|
||||||
</h2>
|
</h2>
|
||||||
<h2>请假: {{ state.selectedSet.size }}人</h2>
|
<h2>请假: {{ state.boardData.attendance.absent.length }}人</h2>
|
||||||
<h3 v-for="(i, index) in state.selectedSet" :key="'absent-' + index">
|
<h3 v-for="(name, index) in state.boardData.attendance.absent" :key="'absent-' + index">
|
||||||
{{ `${index + 1}. ${state.studentList[i]}` }}
|
{{ `${index + 1}. ${name}` }}
|
||||||
</h3>
|
</h3>
|
||||||
<h2>迟到: {{ state.lateSet.size }}人</h2>
|
<h2>迟到: {{ state.boardData.attendance.late.length }}人</h2>
|
||||||
<h3 v-for="(i, index) in state.lateSet" :key="'late-' + index">
|
<h3 v-for="(name, index) in state.boardData.attendance.late" :key="'late-' + index">
|
||||||
{{ `${index + 1}. ${state.studentList[i]}` }}
|
{{ `${index + 1}. ${name}` }}
|
||||||
</h3>
|
</h3>
|
||||||
<h2>不参与: {{ state.excludeSet.size }}人</h2>
|
<h2>不参与: {{ state.boardData.attendance.exclude.length }}人</h2>
|
||||||
<h3 v-for="(i, index) in state.excludeSet" :key="'exclude-' + index">
|
<h3 v-for="(name, index) in state.boardData.attendance.exclude" :key="'exclude-' + index">
|
||||||
{{ `${index + 1}. ${state.studentList[i]}` }}
|
{{ `${index + 1}. ${name}` }}
|
||||||
</h3>
|
</h3>
|
||||||
</v-col>
|
</v-col>
|
||||||
</div>
|
</div>
|
||||||
@ -314,9 +314,14 @@ export default {
|
|||||||
state: {
|
state: {
|
||||||
classNumber: "",
|
classNumber: "",
|
||||||
studentList: [],
|
studentList: [],
|
||||||
selectedSet: new Set(),
|
boardData: {
|
||||||
lateSet: new Set(),
|
homework: {},
|
||||||
excludeSet: new Set(), // 新增不参与集合
|
attendance: {
|
||||||
|
absent: [],
|
||||||
|
late: [],
|
||||||
|
exclude: []
|
||||||
|
}
|
||||||
|
},
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
dialogTitle: "",
|
dialogTitle: "",
|
||||||
textarea: "",
|
textarea: "",
|
||||||
@ -326,7 +331,6 @@ export default {
|
|||||||
contentStyle: { "font-size": `${getSetting("font.size")}px` },
|
contentStyle: { "font-size": `${getSetting("font.size")}px` },
|
||||||
uploadLoading: false,
|
uploadLoading: false,
|
||||||
downloadLoading: false,
|
downloadLoading: false,
|
||||||
homeworkData: {},
|
|
||||||
snackbar: false,
|
snackbar: false,
|
||||||
snackbarText: "",
|
snackbarText: "",
|
||||||
fontSize: getSetting("font.size"),
|
fontSize: getSetting("font.size"),
|
||||||
@ -403,13 +407,13 @@ export default {
|
|||||||
},
|
},
|
||||||
sortedItems() {
|
sortedItems() {
|
||||||
const key = `${JSON.stringify(
|
const key = `${JSON.stringify(
|
||||||
this.state.homeworkData
|
this.state.boardData.homework
|
||||||
)}_${this.state.subjectOrder.join()}_${this.dynamicSort}`;
|
)}_${this.state.subjectOrder.join()}_${this.dynamicSort}`;
|
||||||
if (this.sortedItemsCache.key === key) {
|
if (this.sortedItemsCache.key === key) {
|
||||||
return this.sortedItemsCache.value;
|
return this.sortedItemsCache.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = Object.entries(this.state.homeworkData)
|
const items = Object.entries(this.state.boardData.homework)
|
||||||
.filter(([, value]) => value.content?.trim())
|
.filter(([, value]) => value.content?.trim())
|
||||||
.map(([key, value]) => ({
|
.map(([key, value]) => ({
|
||||||
key,
|
key,
|
||||||
@ -433,7 +437,7 @@ export default {
|
|||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
unusedSubjects() {
|
unusedSubjects() {
|
||||||
const usedKeys = Object.keys(this.state.homeworkData);
|
const usedKeys = Object.keys(this.state.boardData.homework);
|
||||||
return this.state.availableSubjects.filter(
|
return this.state.availableSubjects.filter(
|
||||||
(subject) => !usedKeys.includes(subject.key)
|
(subject) => !usedKeys.includes(subject.key)
|
||||||
);
|
);
|
||||||
@ -555,20 +559,16 @@ export default {
|
|||||||
if (response.error.code === "NOT_FOUND") {
|
if (response.error.code === "NOT_FOUND") {
|
||||||
this.state.showNoDataMessage = true;
|
this.state.showNoDataMessage = true;
|
||||||
this.state.noDataMessage = response.error.message;
|
this.state.noDataMessage = response.error.message;
|
||||||
this.state.homeworkData = {};
|
this.state.boardData = {
|
||||||
this.state.selectedSet = new Set();
|
homework: {},
|
||||||
this.state.lateSet = new Set();
|
attendance: { absent: [], late: [], exclude: [] }
|
||||||
this.state.excludeSet = new Set(); // 添加不参与状态
|
};
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.error.message);
|
throw new Error(response.error.message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 处理成功情况
|
// 处理成功情况
|
||||||
const { homework = {}, attendance = {} } = response.data;
|
this.state.boardData = response.data;
|
||||||
this.state.homeworkData = homework;
|
|
||||||
this.state.selectedSet = new Set(attendance.absent || []);
|
|
||||||
this.state.lateSet = new Set(attendance.late || []);
|
|
||||||
this.state.excludeSet = new Set(attendance.exclude || []); // 添加不参与状态
|
|
||||||
this.state.synced = true;
|
this.state.synced = true;
|
||||||
this.state.showNoDataMessage = false;
|
this.state.showNoDataMessage = false;
|
||||||
this.showMessage("下载成功", "数据已更新");
|
this.showMessage("下载成功", "数据已更新");
|
||||||
@ -588,14 +588,7 @@ export default {
|
|||||||
const response = await dataProvider.saveData(
|
const response = await dataProvider.saveData(
|
||||||
this.provider,
|
this.provider,
|
||||||
this.dataKey,
|
this.dataKey,
|
||||||
{
|
this.state.boardData,
|
||||||
homework: this.state.homeworkData,
|
|
||||||
attendance: {
|
|
||||||
absent: Array.from(this.state.selectedSet),
|
|
||||||
late: Array.from(this.state.lateSet),
|
|
||||||
exclude: Array.from(this.state.excludeSet), // 添加不参与状态
|
|
||||||
},
|
|
||||||
},
|
|
||||||
this.state.dateString
|
this.state.dateString
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -636,7 +629,7 @@ export default {
|
|||||||
|
|
||||||
const content = this.state.textarea.trim();
|
const content = this.state.textarea.trim();
|
||||||
if (content) {
|
if (content) {
|
||||||
this.state.homeworkData[this.currentEditSubject] = {
|
this.state.boardData.homework[this.currentEditSubject] = {
|
||||||
name: this.state.availableSubjects.find(
|
name: this.state.availableSubjects.find(
|
||||||
(s) => s.key === this.currentEditSubject
|
(s) => s.key === this.currentEditSubject
|
||||||
)?.name,
|
)?.name,
|
||||||
@ -648,7 +641,7 @@ export default {
|
|||||||
this.uploadData();
|
this.uploadData();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
delete this.state.homeworkData[this.currentEditSubject];
|
delete this.state.boardData.homework[this.currentEditSubject];
|
||||||
}
|
}
|
||||||
this.state.dialogVisible = false;
|
this.state.dialogVisible = false;
|
||||||
},
|
},
|
||||||
@ -674,16 +667,16 @@ export default {
|
|||||||
}
|
}
|
||||||
this.currentEditSubject = subject;
|
this.currentEditSubject = subject;
|
||||||
// 如果是新科目,需要创建对应的数据结构
|
// 如果是新科目,需要创建对应的数据结构
|
||||||
if (!this.state.homeworkData[subject]) {
|
if (!this.state.boardData.homework[subject]) {
|
||||||
this.state.homeworkData[subject] = {
|
this.state.boardData.homework[subject] = {
|
||||||
name:
|
name:
|
||||||
this.state.availableSubjects.find((s) => s.key === subject)?.name ||
|
this.state.availableSubjects.find((s) => s.key === subject)?.name ||
|
||||||
subject,
|
subject,
|
||||||
content: "",
|
content: "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.state.dialogTitle = this.state.homeworkData[subject].name;
|
this.state.dialogTitle = this.state.boardData.homework[subject].name;
|
||||||
this.state.textarea = this.state.homeworkData[subject].content;
|
this.state.textarea = this.state.boardData.homework[subject].content;
|
||||||
this.state.dialogVisible = true;
|
this.state.dialogVisible = true;
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this.$refs.inputRef) {
|
if (this.$refs.inputRef) {
|
||||||
@ -701,16 +694,17 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
toggleStudentStatus(index) {
|
toggleStudentStatus(index) {
|
||||||
if (this.state.selectedSet.has(index)) {
|
const student = this.state.studentList[index];
|
||||||
this.state.selectedSet.delete(index);
|
if (this.state.boardData.attendance.absent.includes(student)) {
|
||||||
this.state.lateSet.add(index);
|
this.state.boardData.attendance.absent = this.state.boardData.attendance.absent.filter(name => name !== student);
|
||||||
} else if (this.state.lateSet.has(index)) {
|
this.state.boardData.attendance.late.push(student);
|
||||||
this.state.lateSet.delete(index);
|
} else if (this.state.boardData.attendance.late.includes(student)) {
|
||||||
this.state.excludeSet.add(index);
|
this.state.boardData.attendance.late = this.state.boardData.attendance.late.filter(name => name !== student);
|
||||||
} else if (this.state.excludeSet.has(index)) {
|
this.state.boardData.attendance.exclude.push(student);
|
||||||
this.state.excludeSet.delete(index);
|
} else if (this.state.boardData.attendance.exclude.includes(student)) {
|
||||||
|
this.state.boardData.attendance.exclude = this.state.boardData.attendance.exclude.filter(name => name !== student);
|
||||||
} else {
|
} else {
|
||||||
this.state.selectedSet.add(index);
|
this.state.boardData.attendance.absent.push(student);
|
||||||
}
|
}
|
||||||
this.state.synced = false;
|
this.state.synced = false;
|
||||||
if (this.autoSave) {
|
if (this.autoSave) {
|
||||||
@ -719,9 +713,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
cleanstudentslist() {
|
cleanstudentslist() {
|
||||||
this.state.selectedSet.clear();
|
this.state.boardData.attendance.absent = [];
|
||||||
this.state.lateSet.clear();
|
this.state.boardData.attendance.late = [];
|
||||||
this.state.excludeSet.clear();
|
this.state.boardData.attendance.exclude = [];
|
||||||
this.state.synced = false;
|
this.state.synced = false;
|
||||||
if (this.autoSave) {
|
if (this.autoSave) {
|
||||||
this.uploadData();
|
this.uploadData();
|
||||||
@ -865,65 +859,80 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
setAllPresent() {
|
setAllPresent() {
|
||||||
this.state.selectedSet.clear();
|
this.state.boardData.attendance = {
|
||||||
this.state.lateSet.clear();
|
absent: [],
|
||||||
this.state.excludeSet.clear();
|
late: [],
|
||||||
|
exclude: []
|
||||||
|
};
|
||||||
|
this.state.synced = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
setAllAbsent() {
|
setAllAbsent() {
|
||||||
this.state.studentList.forEach((_, index) => {
|
this.state.boardData.attendance.absent = [...this.state.studentList];
|
||||||
this.setAbsent(index);
|
this.state.boardData.attendance.late = [];
|
||||||
});
|
this.state.boardData.attendance.exclude = [];
|
||||||
|
this.state.synced = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
setAllLate() {
|
setAllLate() {
|
||||||
this.state.studentList.forEach((_, index) => {
|
this.state.boardData.attendance.absent = [];
|
||||||
this.setLate(index);
|
this.state.boardData.attendance.late = [...this.state.studentList];
|
||||||
});
|
this.state.boardData.attendance.exclude = [];
|
||||||
|
this.state.synced = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
isPresent(index) {
|
isPresent(index) {
|
||||||
return (
|
const student = this.state.studentList[index];
|
||||||
!this.state.selectedSet.has(index) &&
|
const { absent, late, exclude } = this.state.boardData.attendance;
|
||||||
!this.state.lateSet.has(index) &&
|
return !absent.includes(student) && !late.includes(student) && !exclude.includes(student);
|
||||||
!this.state.excludeSet.has(index)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
isAbsent(index) {
|
isAbsent(index) {
|
||||||
return this.state.selectedSet.has(index);
|
return this.state.boardData.attendance.absent.includes(this.state.studentList[index]);
|
||||||
},
|
},
|
||||||
|
|
||||||
isLate(index) {
|
isLate(index) {
|
||||||
return this.state.lateSet.has(index);
|
return this.state.boardData.attendance.late.includes(this.state.studentList[index]);
|
||||||
},
|
},
|
||||||
|
|
||||||
isExclude(index) {
|
isExclude(index) {
|
||||||
return this.state.excludeSet.has(index);
|
return this.state.boardData.attendance.exclude.includes(this.state.studentList[index]);
|
||||||
},
|
},
|
||||||
|
|
||||||
setPresent(index) {
|
setPresent(index) {
|
||||||
this.state.selectedSet.delete(index);
|
const student = this.state.studentList[index];
|
||||||
this.state.lateSet.delete(index);
|
const { absent, late, exclude } = this.state.boardData.attendance;
|
||||||
this.state.excludeSet.delete(index);
|
this.state.boardData.attendance.absent = absent.filter(name => name !== student);
|
||||||
|
this.state.boardData.attendance.late = late.filter(name => name !== student);
|
||||||
|
this.state.boardData.attendance.exclude = exclude.filter(name => name !== student);
|
||||||
|
this.state.synced = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
setAbsent(index) {
|
setAbsent(index) {
|
||||||
this.state.selectedSet.add(index);
|
const student = this.state.studentList[index];
|
||||||
this.state.lateSet.delete(index);
|
if (!this.state.boardData.attendance.absent.includes(student)) {
|
||||||
this.state.excludeSet.delete(index);
|
this.setPresent(index);
|
||||||
|
this.state.boardData.attendance.absent.push(student);
|
||||||
|
this.state.synced = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setLate(index) {
|
setLate(index) {
|
||||||
this.state.lateSet.add(index);
|
const student = this.state.studentList[index];
|
||||||
this.state.selectedSet.delete(index);
|
if (!this.state.boardData.attendance.late.includes(student)) {
|
||||||
this.state.excludeSet.delete(index);
|
this.setPresent(index);
|
||||||
|
this.state.boardData.attendance.late.push(student);
|
||||||
|
this.state.synced = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setExclude(index) {
|
setExclude(index) {
|
||||||
this.state.excludeSet.add(index);
|
const student = this.state.studentList[index];
|
||||||
this.state.selectedSet.delete(index);
|
if (!this.state.boardData.attendance.exclude.includes(student)) {
|
||||||
this.state.lateSet.delete(index);
|
this.setPresent(index);
|
||||||
|
this.state.boardData.attendance.exclude.push(student);
|
||||||
|
this.state.synced = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async saveAttendance() {
|
async saveAttendance() {
|
||||||
|
26
src/styles/warnings.scss
Normal file
26
src/styles/warnings.scss
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
@mixin warning-card {
|
||||||
|
&.warning {
|
||||||
|
animation: pulse-warning 2s infinite;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: -2px;
|
||||||
|
border: 2px solid rgb(var(--v-theme-warning));
|
||||||
|
border-radius: inherit;
|
||||||
|
animation: pulse-border 2s infinite;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-warning {
|
||||||
|
0%, 100% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.002); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-border {
|
||||||
|
0%, 100% { opacity: 1; }
|
||||||
|
50% { opacity: 0.5; }
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user