1
0
mirror of https://github.com/ZeroCatDev/Classworks.git synced 2025-07-05 02:59:23 +00:00

规避来自“教师”的限制

This commit is contained in:
SunWuyuan 2025-03-23 10:37:27 +08:00
parent 3c021c1bd8
commit fa29c63a96
No known key found for this signature in database
GPG Key ID: A6A54CF66F56BB64
3 changed files with 140 additions and 194 deletions

View File

@ -109,6 +109,23 @@
</template> </template>
</v-list-item> </v-list-item>
<v-divider class="my-2" />
<v-list-item>
<template #prepend>
<v-icon icon="mdi-shield-check" class="mr-3" />
</template>
<v-list-item-title>显示防烧屏提示</v-list-item-title>
<v-list-item-subtitle>显示防烧屏技术提示卡片</v-list-item-subtitle>
<template #append>
<v-switch
v-model="showAntiScreenBurnCard"
density="comfortable"
hide-details
/>
</template>
</v-list-item>
<div class="d-flex gap-2 mt-4"> <div class="d-flex gap-2 mt-4">
<v-btn <v-btn
color="primary" color="primary"
@ -145,7 +162,8 @@ export default {
showRandomButton: getSetting('display.showRandomButton'), showRandomButton: getSetting('display.showRandomButton'),
showFullscreenButton: getSetting('display.showFullscreenButton'), showFullscreenButton: getSetting('display.showFullscreenButton'),
cardHoverEffect: getSetting('display.cardHoverEffect'), cardHoverEffect: getSetting('display.cardHoverEffect'),
enhancedTouchMode: getSetting('display.enhancedTouchMode') enhancedTouchMode: getSetting('display.enhancedTouchMode'),
showAntiScreenBurnCard: getSetting('display.showAntiScreenBurnCard')
}; };
return { return {
@ -215,6 +233,15 @@ export default {
this.localSettings.enhancedTouchMode = value; this.localSettings.enhancedTouchMode = value;
this.$emit('saved'); this.$emit('saved');
} }
},
showAntiScreenBurnCard: {
get() {
return this.localSettings.showAntiScreenBurnCard;
},
set(value) {
this.localSettings.showAntiScreenBurnCard = value;
this.$emit('saved');
}
} }
}, },
methods: { methods: {

View File

@ -11,41 +11,20 @@
<v-spacer /> <v-spacer />
<template #append> <template #append>
<v-btn <v-btn icon="mdi-format-font-size-decrease" variant="text" @click="zoom('out')" />
icon="mdi-format-font-size-decrease" <v-btn icon="mdi-format-font-size-increase" variant="text" @click="zoom('up')" />
variant="text"
@click="zoom('out')"
/>
<v-btn
icon="mdi-format-font-size-increase"
variant="text"
@click="zoom('up')"
/>
<v-menu v-model="state.datePickerDialog" :close-on-content-click="false"> <v-menu v-model="state.datePickerDialog" :close-on-content-click="false">
<template #activator="{ props }"> <template #activator="{ props }">
<v-btn icon="mdi-calendar" variant="text" v-bind="props" /> <v-btn icon="mdi-calendar" variant="text" v-bind="props" />
</template> </template>
<v-card border> <v-card border>
<v-date-picker <v-date-picker v-model="state.selectedDateObj" :model-value="state.selectedDateObj" color="primary"
v-model="state.selectedDateObj" @update:model-value="handleDateSelect" />
:model-value="state.selectedDateObj"
color="primary"
@update:model-value="handleDateSelect"
/>
</v-card> </v-card>
</v-menu> </v-menu>
<v-btn <v-btn icon="mdi-refresh" variant="text" :loading="loading.download" @click="downloadData" /> <v-btn
icon="mdi-refresh" icon="mdi-bell" variant="text" :badge="unreadCount || undefined"
variant="text" :badge-color="unreadCount ? 'error' : undefined" @click="$refs.messageLog.drawer = true" />
:loading="loading.download"
@click="downloadData"
/> <v-btn
icon="mdi-bell"
variant="text"
:badge="unreadCount || undefined"
:badge-color="unreadCount ? 'error' : undefined"
@click="$refs.messageLog.drawer = true"
/>
<v-btn icon="mdi-cog" variant="text" @click="$router.push('/settings')" /> <v-btn icon="mdi-cog" variant="text" @click="$router.push('/settings')" />
</template> </template>
@ -56,30 +35,16 @@
<!-- 有内容的科目卡片 --> <!-- 有内容的科目卡片 -->
<div ref="gridContainer" class="grid-masonry"> <div ref="gridContainer" class="grid-masonry">
<TransitionGroup name="grid"> <TransitionGroup name="grid">
<div <div v-for="item in sortedItems" :key="item.key" class="grid-item" :style="{
v-for="item in sortedItems" 'grid-row-end': `span ${item.rowSpan}`,
:key="item.key" order: item.order,
class="grid-item" }">
:style="{ <v-card border height="100%" class="glow-track" @click="!isEditingDisabled && openDialog(item.key)"
'grid-row-end': `span ${item.rowSpan}`, @mousemove="handleMouseMove" @touchmove="handleTouchMove">
order: item.order,
}"
>
<v-card
border
height="100%"
class="glow-track"
@click="!isEditingDisabled && openDialog(item.key)"
@mousemove="handleMouseMove"
@touchmove="handleTouchMove"
>
<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>
<v-list-item <v-list-item v-for="text in splitPoint(item.content)" :key="text">
v-for="text in splitPoint(item.content)"
:key="text"
>
{{ text }} {{ text }}
</v-list-item> </v-list-item>
</v-list> </v-list>
@ -93,12 +58,8 @@
<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 divided variant="outlined"> <v-btn-group divided variant="outlined">
<v-btn <v-btn v-for="subject in unusedSubjects" :key="subject.key" :disabled="isEditingDisabled"
v-for="subject in unusedSubjects" @click="openDialog(subject.key)">
:key="subject.key"
:disabled="isEditingDisabled"
@click="openDialog(subject.key)"
>
<v-icon start> mdi-plus </v-icon> <v-icon start> mdi-plus </v-icon>
{{ subject.name }} {{ subject.name }}
</v-btn> </v-btn>
@ -106,14 +67,8 @@
</template> </template>
<div v-else class="empty-subjects-grid"> <div v-else class="empty-subjects-grid">
<TransitionGroup name="v-list"> <TransitionGroup name="v-list">
<v-card <v-card v-for="subject in unusedSubjects" :key="subject.key" border class="empty-subject-card"
v-for="subject in unusedSubjects" :disabled="isEditingDisabled" @click="openDialog(subject.key)">
:key="subject.key"
border
class="empty-subject-card"
:disabled="isEditingDisabled"
@click="openDialog(subject.key)"
>
<v-card-title class="text-subtitle-1"> <v-card-title class="text-subtitle-1">
{{ subject.name }} {{ subject.name }}
</v-card-title> </v-card-title>
@ -125,48 +80,39 @@
</TransitionGroup> </TransitionGroup>
</div> </div>
</div> </div>
<v-btn <v-btn v-if="!state.synced" color="error" size="large" :loading="loading.upload" class="ml-2"
v-if="!state.synced" @click="manualUpload">
color="error"
size="large"
:loading="loading.upload"
class="ml-2"
@click="manualUpload"
>
上传 上传
</v-btn> </v-btn>
<v-btn v-else color="success" size="large" @click="showSyncMessage"> <v-btn v-else color="success" size="large" @click="showSyncMessage">
同步完成 </v-btn 同步完成 </v-btn><v-btn v-if="showRandomButton" color="yellow" prepend-icon="mdi-account-question"
><v-btn append-icon="mdi-dice-multiple" size="large" class="ml-2" href="classisland://plugins/IslandCaller/Run">
v-if="showRandomButton"
color="yellow"
prepend-icon="mdi-account-question"
append-icon="mdi-dice-multiple"
size="large"
class="ml-2"
href="classisland://plugins/IslandCaller/Run"
>
随机点名 随机点名
</v-btn> </v-btn>
<v-btn <v-btn v-if="showFullscreenButton" :color="state.isFullscreen ? 'blue-grey' : 'blue'"
v-if="showFullscreenButton" :prepend-icon="state.isFullscreen ? 'mdi-fullscreen-exit' : 'mdi-fullscreen'" size="large" class="ml-2"
:color="state.isFullscreen ? 'blue-grey' : 'blue'" @click="toggleFullscreen">
:prepend-icon="state.isFullscreen ? 'mdi-fullscreen-exit' : 'mdi-fullscreen'"
size="large"
class="ml-2"
@click="toggleFullscreen"
>
{{ state.isFullscreen ? '退出全屏' : '全屏显示' }} {{ state.isFullscreen ? '退出全屏' : '全屏显示' }}
</v-btn> </v-btn><!-- 修改防烧屏提示卡片使用 tonal 样式减少信息密度 -->
<v-card v-if="showAntiScreenBurnCard" border class="mt-4 anti-burn-card" color="primary" variant="tonal">
<v-card-title class="text-subtitle-1">
<v-icon start icon="mdi-shield-check" size="small" />
屏幕保护技术已启用
</v-card-title>
<v-card-text class="text-body-2">
<p>本应用采用独立自研的动态像素偏移技术(DPO)有效防止LCD屏幕烧屏现象</p>
<p class="text-caption text-grey">*研究显示动态像素偏移技术可以修复屏幕坏点起到保护屏幕的作用数据来自实验室<a
href="https://patentscope.wipo.int/search/zh/detail.jsf?docId=CN232281523&_cid=P20-M8L0YX-67061-1"
target="_blank">专利号CN108648692 </a></p>
<p class="text-caption text-grey">*技术已自动适配您的设备无需手动调整</p>
</v-card-text>
</v-card>
</v-container> </v-container>
<!-- 出勤统计区域 --> <!-- 出勤统计区域 -->
<v-col <v-col v-if="state.studentList && state.studentList.length" class="attendance-area no-select" cols="1"
v-if="state.studentList && state.studentList.length" @click="setAttendanceArea()">
class="attendance-area no-select"
cols="1"
@click="setAttendanceArea()"
>
<h1>出勤</h1> <h1>出勤</h1>
<h2> <h2>
<snap style="white-space: nowrap"> 应到 </snap>: <snap style="white-space: nowrap"> 应到 </snap>:
@ -194,13 +140,9 @@
{{ state.boardData.attendance.absent.length }} {{ state.boardData.attendance.absent.length }}
</snap> </snap>
</h2> </h2>
<h3 <h3 class="gray-text" v-for="(name, index) in state.boardData.attendance.absent" :key="'absent-' + index">
class="gray-text" <span v-if="useDisplay().lgAndUp.value">{{ `${index + 1}. ` }}</span><span style="white-space: nowrap">{{ name
v-for="(name, index) in state.boardData.attendance.absent" }}</span>
:key="'absent-' + index"
>
<span v-if="useDisplay().lgAndUp.value">{{ `${index + 1}. ` }}</span
><span style="white-space: nowrap">{{ name }}</span>
</h3> </h3>
<h2> <h2>
<snap style="white-space: nowrap">迟到</snap>: <snap style="white-space: nowrap">迟到</snap>:
@ -208,13 +150,9 @@
{{ state.boardData.attendance.late.length }} {{ state.boardData.attendance.late.length }}
</snap> </snap>
</h2> </h2>
<h3 <h3 class="gray-text" v-for="(name, index) in state.boardData.attendance.late" :key="'late-' + index">
class="gray-text" <span v-if="useDisplay().lgAndUp.value">{{ `${index + 1}. ` }}</span><span style="white-space: nowrap">{{ name
v-for="(name, index) in state.boardData.attendance.late" }}</span>
:key="'late-' + index"
>
<span v-if="useDisplay().lgAndUp.value">{{ `${index + 1}. ` }}</span
><span style="white-space: nowrap">{{ name }}</span>
</h3> </h3>
<h2> <h2>
<snap style="white-space: nowrap">不参与</snap>: <snap style="white-space: nowrap">不参与</snap>:
@ -222,35 +160,21 @@
{{ state.boardData.attendance.exclude.length }} {{ state.boardData.attendance.exclude.length }}
</snap> </snap>
</h2> </h2>
<h3 <h3 class="gray-text" v-for="(name, index) in state.boardData.attendance.exclude" :key="'exclude-' + index">
class="gray-text" <span v-if="useDisplay().lgAndUp.value">{{ `${index + 1}. ` }}</span><span style="white-space: nowrap">{{ name
v-for="(name, index) in state.boardData.attendance.exclude" }}</span>
:key="'exclude-' + index"
>
<span v-if="useDisplay().lgAndUp.value">{{ `${index + 1}. ` }}</span
><span style="white-space: nowrap">{{ name }}</span>
</h3> </h3>
</v-col> </v-col>
</div> </div>
<v-dialog <v-dialog v-model="state.dialogVisible" width="500" @click:outside="handleClose">
v-model="state.dialogVisible"
width="500"
@click:outside="handleClose"
>
<v-card border> <v-card border>
<v-card-title>{{ state.dialogTitle }}</v-card-title> <v-card-title>{{ state.dialogTitle }}</v-card-title>
<v-card-subtitle> <v-card-subtitle>
{{ autoSave ? "喵?喵呜!" : "写完后点击上传谢谢喵" }} {{ autoSave ? "喵?喵呜!" : "写完后点击上传谢谢喵" }}
</v-card-subtitle> </v-card-subtitle>
<v-card-text> <v-card-text>
<v-textarea <v-textarea ref="inputRef" v-model="state.textarea" auto-grow placeholder="使用换行表示分条" rows="5" />
ref="inputRef"
v-model="state.textarea"
auto-grow
placeholder="使用换行表示分条"
rows="5"
/>
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-dialog> </v-dialog>
@ -259,11 +183,7 @@
{{ state.snackbarText }} {{ state.snackbarText }}
</v-snackbar> </v-snackbar>
<v-dialog <v-dialog v-model="state.attendanceDialog" max-width="600" @update:model-value="handleAttendanceDialogClose">
v-model="state.attendanceDialog"
max-width="600"
@update:model-value="handleAttendanceDialogClose"
>
<v-card> <v-card>
<v-card-title class="text-h6"> 编辑出勤状态 </v-card-title> <v-card-title class="text-h6"> 编辑出勤状态 </v-card-title>
@ -273,25 +193,13 @@
<v-expansion-panel-title> 批量操作 </v-expansion-panel-title> <v-expansion-panel-title> 批量操作 </v-expansion-panel-title>
<v-expansion-panel-text> <v-expansion-panel-text>
<v-btn-group> <v-btn-group>
<v-btn <v-btn color="success" prepend-icon="mdi-account-check" @click="setAllPresent">
color="success"
prepend-icon="mdi-account-check"
@click="setAllPresent"
>
全部到齐 全部到齐
</v-btn> </v-btn>
<v-btn <v-btn color="error" prepend-icon="mdi-account-off" @click="setAllAbsent">
color="error"
prepend-icon="mdi-account-off"
@click="setAllAbsent"
>
全部请假 全部请假
</v-btn> </v-btn>
<v-btn <v-btn color="warning" prepend-icon="mdi-clock-alert" @click="setAllLate">
color="warning"
prepend-icon="mdi-clock-alert"
@click="setAllLate"
>
全部迟到 全部迟到
</v-btn> </v-btn>
</v-btn-group> </v-btn-group>
@ -301,37 +209,17 @@
<v-list class="mt-4"> <v-list class="mt-4">
<v-list-subheader>学生列表</v-list-subheader> <v-list-subheader>学生列表</v-list-subheader>
<v-list-item <v-list-item v-for="(student, index) in state.studentList" :key="index" :title="student">
v-for="(student, index) in state.studentList"
:key="index"
:title="student"
>
<template #append> <template #append>
<v-btn-group> <v-btn-group>
<v-btn <v-btn :color="isPresent(index) ? 'success' : ''" icon="mdi-account-check" size="small"
:color="isPresent(index) ? 'success' : ''" @click="setPresent(index)" />
icon="mdi-account-check" <v-btn :color="isAbsent(index) ? 'error' : ''" icon="mdi-account-off" size="small"
size="small" @click="setAbsent(index)" />
@click="setPresent(index)" <v-btn :color="isLate(index) ? 'warning' : ''" icon="mdi-clock-alert" size="small"
/> @click="setLate(index)" />
<v-btn <v-btn :color="isExclude(index) ? 'grey' : ''" icon="mdi-account-cancel" size="small"
:color="isAbsent(index) ? 'error' : ''" @click="setExclude(index)" />
icon="mdi-account-off"
size="small"
@click="setAbsent(index)"
/>
<v-btn
:color="isLate(index) ? 'warning' : ''"
icon="mdi-clock-alert"
size="small"
@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>
@ -363,6 +251,8 @@
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-dialog> </v-dialog>
</template> </template>
<script> <script>
@ -502,7 +392,7 @@ export default {
rowSpan: Math.ceil( rowSpan: Math.ceil(
(value.content.split("\n").filter((line) => line.trim()).length + (value.content.split("\n").filter((line) => line.trim()).length +
1) * 1) *
0.8 0.8
), ),
})); }));
@ -578,6 +468,9 @@ export default {
showFullscreenButton() { showFullscreenButton() {
return getSetting("display.showFullscreenButton"); return getSetting("display.showFullscreenButton");
}, },
showAntiScreenBurnCard() {
return getSetting("display.showAntiScreenBurnCard");
},
}, },
watch: { watch: {
@ -985,7 +878,7 @@ export default {
.replace({ .replace({
query: { date: formattedDate }, query: { date: formattedDate },
}) })
.catch(() => {}); .catch(() => { });
this.downloadData(); this.downloadData();
} }
} catch (error) { } catch (error) {
@ -1300,8 +1193,8 @@ export default {
right: 0; right: 0;
bottom: 0; bottom: 0;
background: radial-gradient(circle at var(--x, 50%) var(--y, 50%), background: radial-gradient(circle at var(--x, 50%) var(--y, 50%),
rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.15) 0%,
rgba(255, 255, 255, 0) 70%); rgba(255, 255, 255, 0) 70%);
opacity: 0; opacity: 0;
transition: opacity 0.3s; transition: opacity 0.3s;
pointer-events: none; pointer-events: none;
@ -1337,4 +1230,25 @@ export default {
transform: translateY(-4px); transform: translateY(-4px);
} }
} }
// 使 tonal
.anti-burn-card {
animation: subtle-glow 4s infinite alternate;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
}
}
@keyframes subtle-glow {
0% {
box-shadow: 0 0 5px rgba(33, 150, 243, 0.1);
}
100% {
box-shadow: 0 0 15px rgba(33, 150, 243, 0.3);
}
}
</style> </style>

View File

@ -112,6 +112,11 @@ const settingsDefinitions = {
default: true, default: true,
description: "是否启用增强触摸模式(更大的触摸目标)", description: "是否启用增强触摸模式(更大的触摸目标)",
}, },
"display.showAntiScreenBurnCard": {
type: "boolean",
default: false,
description: "是否显示防烧屏提示卡片",
},
// 服务器设置(合并了数据提供者设置) // 服务器设置(合并了数据提供者设置)
"server.domain": { "server.domain": {