1
0
mirror of https://github.com/ZeroCatDev/Classworks.git synced 2025-07-03 01:39:22 +00:00
This commit is contained in:
SunWuyuan 2025-03-09 15:36:01 +08:00
parent 39d6bfec60
commit 8aaf537f84
No known key found for this signature in database
GPG Key ID: A6A54CF66F56BB64
2 changed files with 108 additions and 146 deletions

View File

@ -246,27 +246,42 @@ export default {
index: -1, index: -1,
name: '' name: ''
}, },
savedState: null // null savedState: { // null
list: [],
text: ''
}
} }
}, },
created() { created() {
// 使 modelValue // mounted
},
mounted() {
// 使 nextTick DOM
this.$nextTick(() => {
this.savedState = { this.savedState = {
list: [...this.modelValue.list], list: [...this.modelValue.list],
text: this.modelValue.text text: this.modelValue.text
} }
})
}, },
watch: { watch: {
originalList: { originalList: {
handler(newList) { handler(newList) {
// //
if (!this.savedState || this.savedState.list.length === 0) { if (this.modelValue.list.length === 0) {
this.savedState = { const newState = {
list: [...newList], list: [...newList],
text: newList.join('\n') text: newList.join('\n')
} };
//
this.savedState = { ...newState };
this.updateModelValue({
...this.modelValue,
...newState
});
} }
}, },
immediate: true immediate: true
@ -285,14 +300,13 @@ export default {
} }
}, },
hasChanges() { hasChanges() {
if (!this.savedState) return false;
const currentState = JSON.stringify({ const currentState = JSON.stringify({
list: this.modelValue.list, list: this.modelValue.list,
text: this.modelValue.text text: this.modelValue.text
}); });
const savedState = JSON.stringify(this.savedState); const savedState = JSON.stringify(this.savedState);
return currentState !== savedState; // savedState
return this.savedState.list.length > 0 && currentState !== savedState;
} }
}, },
@ -329,6 +343,14 @@ export default {
}); });
}, },
//
updateSavedState() {
this.savedState = {
list: [...this.modelValue.list],
text: this.modelValue.text
};
},
// //
addStudent() { addStudent() {
const name = this.newStudentName.trim(); const name = this.newStudentName.trim();
@ -401,12 +423,12 @@ export default {
// //
async handleSave() { async handleSave() {
try {
await this.$emit('save'); await this.$emit('save');
// this.updateSavedState();
this.savedState = { } catch (error) {
list: [...this.modelValue.list], console.error('保存失败:', error);
text: this.modelValue.text }
};
}, },
handleTextInput(value) { handleTextInput(value) {
@ -417,7 +439,7 @@ export default {
this.updateModelValue({ this.updateModelValue({
text: value, text: value,
list: list list
}); });
} }
} }

View File

@ -21,16 +21,9 @@
variant="text" variant="text"
@click="zoom('up')" @click="zoom('up')"
/> />
<v-menu <v-menu v-model="state.datePickerDialog" :close-on-content-click="false">
v-model="state.datePickerDialog"
:close-on-content-click="false"
>
<template #activator="{ props }"> <template #activator="{ props }">
<v-btn <v-btn icon="mdi-calendar" variant="text" v-bind="props" />
icon="mdi-calendar"
variant="text"
v-bind="props"
/>
</template> </template>
<v-date-picker <v-date-picker
@ -45,11 +38,7 @@
:loading="loading.download" :loading="loading.download"
@click="downloadData" @click="downloadData"
/> />
<v-btn <v-btn icon="mdi-cog" variant="text" @click="$router.push('/settings')" />
icon="mdi-cog"
variant="text"
@click="$router.push('/settings')"
/>
<v-btn <v-btn
icon="mdi-bell" icon="mdi-bell"
variant="text" variant="text"
@ -61,15 +50,9 @@
</v-app-bar> </v-app-bar>
<div class="d-flex"> <div class="d-flex">
<!-- 主要内容区域 --> <!-- 主要内容区域 -->
<v-container <v-container class="main-window flex-grow-1" fluid>
class="main-window flex-grow-1"
fluid
>
<!-- 有内容的科目卡片 --> <!-- 有内容的科目卡片 -->
<div <div ref="gridContainer" class="grid-masonry">
ref="gridContainer"
class="grid-masonry"
>
<div <div
v-for="item in sortedItems" v-for="item in sortedItems"
:key="item.key" :key="item.key"
@ -80,10 +63,7 @@
}" }"
@click="!isEditingDisabled && openDialog(item.key)" @click="!isEditingDisabled && openDialog(item.key)"
> >
<v-card <v-card border height="100%">
border
height="100%"
>
<v-card-title>{{ item.name }}</v-card-title> <v-card-title>{{ item.name }}</v-card-title>
<v-card-text :style="state.contentStyle"> <v-card-text :style="state.contentStyle">
<v-list> <v-list>
@ -102,26 +82,19 @@
<!-- 单独显示空科目 --> <!-- 单独显示空科目 -->
<div class="empty-subjects mt-4"> <div class="empty-subjects mt-4">
<template v-if="emptySubjectDisplay === 'button'"> <template v-if="emptySubjectDisplay === 'button'">
<v-btn-group class="gap-2 flex-wrap"> <v-btn-group divided variant="outlined">
<v-btn <v-btn
v-for="subject in unusedSubjects" v-for="subject in unusedSubjects"
:key="subject.key" :key="subject.key"
variant="tonal"
color="primary"
:disabled="isEditingDisabled" :disabled="isEditingDisabled"
@click="openDialog(subject.key)" @click="openDialog(subject.key)"
> >
<v-icon start> <v-icon start> mdi-plus </v-icon>
mdi-plus
</v-icon>
{{ subject.name }} {{ subject.name }}
</v-btn> </v-btn>
</v-btn-group> </v-btn-group>
</template> </template>
<div <div v-else class="empty-subjects-grid">
v-else
class="empty-subjects-grid"
>
<v-card <v-card
v-for="subject in unusedSubjects" v-for="subject in unusedSubjects"
:key="subject.key" :key="subject.key"
@ -134,15 +107,8 @@
{{ subject.name }} {{ subject.name }}
</v-card-title> </v-card-title>
<v-card-text class="text-center"> <v-card-text class="text-center">
<v-icon <v-icon size="small" color="grey"> mdi-plus </v-icon>
size="small" <div class="text-caption text-grey">点击添加作业</div>
color="grey"
>
mdi-plus
</v-icon>
<div class="text-caption text-grey">
点击添加作业
</div>
</v-card-text> </v-card-text>
</v-card> </v-card>
</div> </div>
@ -157,20 +123,26 @@
@click="setAttendanceArea()" @click="setAttendanceArea()"
> >
<h1>出勤</h1> <h1>出勤</h1>
<h2>应到: {{ state.studentList.length }}</h2> <h2>应到: {{ state.studentList.length - state.excludeSet.size }}</h2>
<h2>实到: {{ state.studentList.length - state.selectedSet.size }}</h2> <h2>
实到:
{{
state.studentList.length -
state.selectedSet.size -
state.lateSet.size -
state.excludeSet.size
}}
</h2>
<h2>请假: {{ state.selectedSet.size }}</h2> <h2>请假: {{ state.selectedSet.size }}</h2>
<h3 <h3 v-for="(i, index) in state.selectedSet" :key="'absent-' + index">
v-for="(i, index) in state.selectedSet"
:key="'absent-' + index"
>
{{ `${index + 1}. ${state.studentList[i]}` }} {{ `${index + 1}. ${state.studentList[i]}` }}
</h3> </h3>
<h2>迟到: {{ state.lateSet.size }}</h2> <h2>迟到: {{ state.lateSet.size }}</h2>
<h3 <h3 v-for="(i, index) in state.lateSet" :key="'late-' + index">
v-for="(i, index) in state.lateSet" {{ `${index + 1}. ${state.studentList[i]}` }}
:key="'late-' + index" </h3>
> <h2>不参与: {{ state.excludeSet.size }}</h2>
<h3 v-for="(i, index) in state.excludeSet" :key="'exclude-' + index">
{{ `${index + 1}. ${state.studentList[i]}` }} {{ `${index + 1}. ${state.studentList[i]}` }}
</h3> </h3>
</v-col> </v-col>
@ -186,14 +158,9 @@
> >
上传 上传
</v-btn> </v-btn>
<v-btn <v-btn v-else color="success" size="large" @click="showSyncMessage">
v-else 同步完成 </v-btn
color="success" ><v-btn
size="large"
@click="showSyncMessage"
>
同步完成
</v-btn><v-btn
v-if="showRandomButton" v-if="showRandomButton"
color="yellow" color="yellow"
prepend-icon="mdi-account-question" prepend-icon="mdi-account-question"
@ -227,65 +194,13 @@
</v-card> </v-card>
</v-dialog> </v-dialog>
<v-dialog <v-snackbar v-model="state.snackbar" :timeout="2000">
v-model="state.attendDialogVisible"
width="800"
>
<v-card>
<v-card-title>设置学生出勤状态</v-card-title>
<v-card-text>
<v-btn @click="cleanstudentslist">
全勤
</v-btn>
<v-container fluid>
<v-row>
<v-col
v-for="(name, i) in state.studentList"
:key="i"
cols="12"
sm="6"
md="4"
>
<v-card
border
:class="{
selected: state.selectedSet.has(i) || state.lateSet.has(i),
}"
:color="
state.selectedSet.has(i)
? 'primary'
: state.lateSet.has(i)
? 'orange'
: ''
"
@click="toggleStudentStatus(i)"
>
<v-card-text>
{{ `${i + 1}. ${name}` }}
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-container>
</v-card-text>
</v-card>
</v-dialog>
<v-snackbar
v-model="state.snackbar"
:timeout="2000"
>
{{ state.snackbarText }} {{ state.snackbarText }}
</v-snackbar> </v-snackbar>
<v-dialog <v-dialog v-model="state.attendanceDialog" max-width="600">
v-model="state.attendanceDialog"
max-width="600"
>
<v-card> <v-card>
<v-card-title class="text-h6"> <v-card-title class="text-h6"> 编辑出勤状态 </v-card-title>
编辑出勤状态
</v-card-title>
<v-card-text> <v-card-text>
<v-expansion-panels> <v-expansion-panels>
@ -346,6 +261,12 @@
size="small" size="small"
@click="setLate(index)" @click="setLate(index)"
/> />
<v-btn
:color="isExclude(index) ? 'grey' : ''"
icon="mdi-account-cancel"
size="small"
@click="setExclude(index)"
/>
</v-btn-group> </v-btn-group>
</template> </template>
</v-list-item> </v-list-item>
@ -354,12 +275,7 @@
<v-card-actions> <v-card-actions>
<v-spacer /> <v-spacer />
<v-btn <v-btn color="primary" @click="saveAttendance"> 保存 </v-btn>
color="primary"
@click="saveAttendance"
>
保存
</v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog> </v-dialog>
@ -389,6 +305,7 @@ export default {
studentList: [], studentList: [],
selectedSet: new Set(), selectedSet: new Set(),
lateSet: new Set(), lateSet: new Set(),
excludeSet: new Set(), //
dialogVisible: false, dialogVisible: false,
dialogTitle: "", dialogTitle: "",
textarea: "", textarea: "",
@ -630,6 +547,7 @@ export default {
this.state.homeworkData = {}; this.state.homeworkData = {};
this.state.selectedSet = new Set(); this.state.selectedSet = new Set();
this.state.lateSet = new Set(); this.state.lateSet = new Set();
this.state.excludeSet = new Set(); //
} else { } else {
throw new Error(response.error.message); throw new Error(response.error.message);
} }
@ -639,6 +557,7 @@ export default {
this.state.homeworkData = homework; this.state.homeworkData = homework;
this.state.selectedSet = new Set(attendance.absent || []); this.state.selectedSet = new Set(attendance.absent || []);
this.state.lateSet = new Set(attendance.late || []); 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("下载成功", "数据已更新");
@ -663,6 +582,7 @@ export default {
attendance: { attendance: {
absent: Array.from(this.state.selectedSet), absent: Array.from(this.state.selectedSet),
late: Array.from(this.state.lateSet), late: Array.from(this.state.lateSet),
exclude: Array.from(this.state.excludeSet), //
}, },
}, },
this.state.dateString this.state.dateString
@ -766,7 +686,7 @@ export default {
}, },
setAttendanceArea() { setAttendanceArea() {
this.state.attendDialogVisible = true; this.state.attendanceDialog = true;
}, },
toggleStudentStatus(index) { toggleStudentStatus(index) {
@ -775,6 +695,9 @@ export default {
this.state.lateSet.add(index); this.state.lateSet.add(index);
} else if (this.state.lateSet.has(index)) { } else if (this.state.lateSet.has(index)) {
this.state.lateSet.delete(index); this.state.lateSet.delete(index);
this.state.excludeSet.add(index);
} else if (this.state.excludeSet.has(index)) {
this.state.excludeSet.delete(index);
} else { } else {
this.state.selectedSet.add(index); this.state.selectedSet.add(index);
} }
@ -787,6 +710,7 @@ export default {
cleanstudentslist() { cleanstudentslist() {
this.state.selectedSet.clear(); this.state.selectedSet.clear();
this.state.lateSet.clear(); this.state.lateSet.clear();
this.state.excludeSet.clear();
this.state.synced = false; this.state.synced = false;
if (this.autoSave) { if (this.autoSave) {
this.uploadData(); this.uploadData();
@ -932,6 +856,7 @@ export default {
setAllPresent() { setAllPresent() {
this.state.selectedSet.clear(); this.state.selectedSet.clear();
this.state.lateSet.clear(); this.state.lateSet.clear();
this.state.excludeSet.clear();
}, },
setAllAbsent() { setAllAbsent() {
@ -948,7 +873,9 @@ export default {
isPresent(index) { isPresent(index) {
return ( return (
!this.state.selectedSet.has(index) && !this.state.lateSet.has(index) !this.state.selectedSet.has(index) &&
!this.state.lateSet.has(index) &&
!this.state.excludeSet.has(index)
); );
}, },
@ -960,19 +887,32 @@ export default {
return this.state.lateSet.has(index); return this.state.lateSet.has(index);
}, },
isExclude(index) {
return this.state.excludeSet.has(index);
},
setPresent(index) { setPresent(index) {
this.state.selectedSet.delete(index); this.state.selectedSet.delete(index);
this.state.lateSet.delete(index); this.state.lateSet.delete(index);
this.state.excludeSet.delete(index);
}, },
setAbsent(index) { setAbsent(index) {
this.state.selectedSet.add(index); this.state.selectedSet.add(index);
this.state.lateSet.delete(index); this.state.lateSet.delete(index);
this.state.excludeSet.delete(index);
}, },
setLate(index) { setLate(index) {
this.state.lateSet.add(index); this.state.lateSet.add(index);
this.state.selectedSet.delete(index); this.state.selectedSet.delete(index);
this.state.excludeSet.delete(index);
},
setExclude(index) {
this.state.excludeSet.add(index);
this.state.selectedSet.delete(index);
this.state.lateSet.delete(index);
}, },
async saveAttendance() { async saveAttendance() {