1
0
mirror of https://github.com/ZeroCatDev/Classworks.git synced 2025-07-04 10:29:23 +00:00
This commit is contained in:
SunWuyuan 2025-03-23 10:20:33 +08:00
parent ac417b1432
commit 3c021c1bd8
No known key found for this signature in database
GPG Key ID: A6A54CF66F56BB64
6 changed files with 414 additions and 38 deletions

View File

@ -1,25 +1,26 @@
<template> <template>
<settings-card <settings-card
title="显示设置" title="显示设置"
icon="mdi-monitor-dashboard" icon="mdi-monitor"
border
> >
<v-form v-model="isValid" @submit.prevent="save"> <v-form v-model="isValid" @submit.prevent="save">
<v-list> <v-list>
<v-list-item> <v-list-item>
<template #prepend> <template #prepend>
<v-icon icon="mdi-eye" class="mr-3" /> <v-icon icon="mdi-card-outline" class="mr-3" />
</template> </template>
<v-list-item-title>空科目显示</v-list-item-title> <v-list-item-title>空科目显示方式</v-list-item-title>
<v-list-item-subtitle>选择空科目的显示方式</v-list-item-subtitle> <v-list-item-subtitle>选择空科目的显示方式</v-list-item-subtitle>
<template #append> <template #append>
<v-btn-toggle <v-select
v-model="localSettings.emptySubjectDisplay" v-model="emptySubjectDisplay"
density="comfortable" :items="displayOptions"
color="primary" density="compact"
> hide-details
<v-btn value="button" :ripple="false">按钮</v-btn> variant="outlined"
<v-btn value="card" :ripple="false">卡片</v-btn> style="max-width: 150px"
</v-btn-toggle> />
</template> </template>
</v-list-item> </v-list-item>
@ -27,14 +28,13 @@
<v-list-item> <v-list-item>
<template #prepend> <template #prepend>
<v-icon icon="mdi-sort" class="mr-3" /> <v-icon icon="mdi-sort-variant" class="mr-3" />
</template> </template>
<v-list-item-title>动态排序</v-list-item-title> <v-list-item-title>动态排序</v-list-item-title>
<v-list-item-subtitle>根据科目动态排序</v-list-item-subtitle> <v-list-item-subtitle>优化卡片布局以提高显示效果</v-list-item-subtitle>
<template #append> <template #append>
<v-switch <v-switch
disabled v-model="dynamicSort"
v-model="localSettings.dynamicSort"
density="comfortable" density="comfortable"
hide-details hide-details
/> />
@ -47,11 +47,11 @@
<template #prepend> <template #prepend>
<v-icon icon="mdi-dice-multiple" class="mr-3" /> <v-icon icon="mdi-dice-multiple" class="mr-3" />
</template> </template>
<v-list-item-title>随机点名按钮</v-list-item-title> <v-list-item-title>显示随机按钮</v-list-item-title>
<v-list-item-subtitle>指向IslandCaller的链接</v-list-item-subtitle> <v-list-item-subtitle>在主页显示随机点名按钮</v-list-item-subtitle>
<template #append> <template #append>
<v-switch <v-switch
v-model="localSettings.showRandomButton" v-model="showRandomButton"
density="comfortable" density="comfortable"
hide-details hide-details
/> />
@ -65,10 +65,44 @@
<v-icon icon="mdi-fullscreen" class="mr-3" /> <v-icon icon="mdi-fullscreen" class="mr-3" />
</template> </template>
<v-list-item-title>显示全屏按钮</v-list-item-title> <v-list-item-title>显示全屏按钮</v-list-item-title>
<v-list-item-subtitle>在主页显示全屏切换按钮</v-list-item-subtitle> <v-list-item-subtitle>在主页显示全屏切换按钮</v-list-item-subtitle>
<template #append> <template #append>
<v-switch <v-switch
v-model="localSettings.showFullscreenButton" v-model="showFullscreenButton"
density="comfortable"
hide-details
/>
</template>
</v-list-item>
<v-divider class="my-2" />
<v-list-item>
<template #prepend>
<v-icon icon="mdi-cards-outline" 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="cardHoverEffect"
density="comfortable"
hide-details
/>
</template>
</v-list-item>
<v-divider class="my-2" />
<v-list-item>
<template #prepend>
<v-icon icon="mdi-gesture-tap" 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="enhancedTouchMode"
density="comfortable" density="comfortable"
hide-details hide-details
/> />
@ -109,18 +143,78 @@ export default {
emptySubjectDisplay: getSetting('display.emptySubjectDisplay'), emptySubjectDisplay: getSetting('display.emptySubjectDisplay'),
dynamicSort: getSetting('display.dynamicSort'), dynamicSort: getSetting('display.dynamicSort'),
showRandomButton: getSetting('display.showRandomButton'), showRandomButton: getSetting('display.showRandomButton'),
showFullscreenButton: getSetting('display.showFullscreenButton') showFullscreenButton: getSetting('display.showFullscreenButton'),
cardHoverEffect: getSetting('display.cardHoverEffect'),
enhancedTouchMode: getSetting('display.enhancedTouchMode')
}; };
return { return {
localSettings: { ...settings }, localSettings: { ...settings },
originalSettings: settings, originalSettings: settings,
isValid: true isValid: true,
displayOptions: [
{ title: '卡片', value: 'card' },
{ title: '按钮', value: 'button' }
]
}; };
}, },
computed: { computed: {
hasChanges() { hasChanges() {
return JSON.stringify(this.localSettings) !== JSON.stringify(this.originalSettings); return JSON.stringify(this.localSettings) !== JSON.stringify(this.originalSettings);
},
emptySubjectDisplay: {
get() {
return this.localSettings.emptySubjectDisplay;
},
set(value) {
this.localSettings.emptySubjectDisplay = value;
this.$emit('saved');
}
},
dynamicSort: {
get() {
return this.localSettings.dynamicSort;
},
set(value) {
this.localSettings.dynamicSort = value;
this.$emit('saved');
}
},
showRandomButton: {
get() {
return this.localSettings.showRandomButton;
},
set(value) {
this.localSettings.showRandomButton = value;
this.$emit('saved');
}
},
showFullscreenButton: {
get() {
return this.localSettings.showFullscreenButton;
},
set(value) {
this.localSettings.showFullscreenButton = value;
this.$emit('saved');
}
},
cardHoverEffect: {
get() {
return this.localSettings.cardHoverEffect;
},
set(value) {
this.localSettings.cardHoverEffect = value;
this.$emit('saved');
}
},
enhancedTouchMode: {
get() {
return this.localSettings.enhancedTouchMode;
},
set(value) {
this.localSettings.enhancedTouchMode = value;
this.$emit('saved');
}
} }
}, },
methods: { methods: {

View File

@ -373,6 +373,7 @@ import { useDisplay } from "vuetify";
import "../styles/index.scss"; import "../styles/index.scss";
import "../styles/transitions.scss"; // import "../styles/transitions.scss"; //
import { debounce, throttle } from "@/utils/debounce"; import { debounce, throttle } from "@/utils/debounce";
import '../styles/global.scss';
export default { export default {
name: "Classworks 作业板", name: "Classworks 作业板",
@ -514,7 +515,9 @@ export default {
return result; return result;
}, },
unusedSubjects() { unusedSubjects() {
const usedKeys = Object.keys(this.state.boardData.homework); const usedKeys = Object.keys(this.state.boardData.homework).filter(
key => this.state.boardData.homework[key].content?.trim()
);
return this.state.availableSubjects.filter( return this.state.availableSubjects.filter(
(subject) => !usedKeys.includes(subject.key) (subject) => !usedKeys.includes(subject.key)
); );
@ -784,14 +787,11 @@ export default {
// () // ()
if (content !== originalContent.trim()) { if (content !== originalContent.trim()) {
if (content) { //
// 使 this.state.boardData.homework[this.currentEditSubject] = {
this.state.boardData.homework[this.currentEditSubject] = { content: content,
content, };
};
} else {
delete this.state.boardData.homework[this.currentEditSubject];
}
this.state.synced = false; this.state.synced = false;
// //
@ -1181,8 +1181,8 @@ export default {
const rect = card.getBoundingClientRect(); const rect = card.getBoundingClientRect();
const x = ((e.clientX - rect.left) / rect.width) * 100; const x = ((e.clientX - rect.left) / rect.width) * 100;
const y = ((e.clientY - rect.top) / rect.height) * 100; const y = ((e.clientY - rect.top) / rect.height) * 100;
card.style.setProperty("--x", `${x}%`); card.style.setProperty('--x', `${x}%`);
card.style.setProperty("--y", `${y}%`); card.style.setProperty('--y', `${y}%`);
}, },
handleTouchMove(e) { handleTouchMove(e) {
@ -1192,8 +1192,8 @@ export default {
const rect = card.getBoundingClientRect(); const rect = card.getBoundingClientRect();
const x = ((touch.clientX - rect.left) / rect.width) * 100; const x = ((touch.clientX - rect.left) / rect.width) * 100;
const y = ((touch.clientY - rect.top) / rect.height) * 100; const y = ((touch.clientY - rect.top) / rect.height) * 100;
card.style.setProperty("--x", `${x}%`); card.style.setProperty('--x', `${x}%`);
card.style.setProperty("--y", `${y}%`); card.style.setProperty('--y', `${y}%`);
} }
}, },
@ -1285,8 +1285,56 @@ export default {
}, },
}; };
</script> </script>
<style scoped> <style lang="scss">
.gray-text { //
opacity: 0.6; .glow-track {
position: relative;
overflow: hidden;
transition: all 0.3s ease;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(circle at var(--x, 50%) var(--y, 50%),
rgba(255, 255, 255, 0.15) 0%,
rgba(255, 255, 255, 0) 70%);
opacity: 0;
transition: opacity 0.3s;
pointer-events: none;
z-index: 1;
}
&:hover::before {
opacity: 1;
}
}
//
.grid-item .v-card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15) !important;
}
&:active {
transform: translateY(-2px);
}
}
//
.empty-subject-card {
transition: all 0.3s ease;
opacity: 0.8;
&:hover {
opacity: 1;
transform: translateY(-4px);
}
} }
</style> </style>

65
src/styles/cards.scss Normal file
View File

@ -0,0 +1,65 @@
// 触摸友好的卡片样式
.touch-card {
border-radius: 16px;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
.v-card-title {
font-size: 1.25rem;
padding: 16px 20px;
}
.v-card-text {
padding: 16px 20px;
}
.v-card-actions {
padding: 12px 20px;
}
&:active {
transform: scale(0.98);
}
}
// 卡片发光效果
.glow-card {
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(circle at var(--x, 50%) var(--y, 50%),
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0) 60%);
opacity: 0;
transition: opacity 0.5s;
pointer-events: none;
}
&:hover::after {
opacity: 1;
}
}
// 网格布局优化
.grid-masonry {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-auto-rows: 20px;
grid-gap: 16px;
}
// 空科目网格
.empty-subjects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-gap: 16px;
margin-top: 16px;
}

86
src/styles/global.scss Normal file
View File

@ -0,0 +1,86 @@
// 全局 UI 美化样式
// 卡片悬浮效果
.hover-card {
transition: transform 0.2s ease, box-shadow 0.3s ease;
will-change: transform, box-shadow;
&:hover, &:focus {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15) !important;
}
&:active {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important;
}
}
// 触摸友好的按钮
.touch-button {
min-height: 48px;
min-width: 48px;
border-radius: 12px;
padding: 12px 24px;
&.v-btn--icon {
min-height: 56px;
min-width: 56px;
}
}
// 波纹效果增强
.ripple-enhanced {
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: radial-gradient(circle at var(--x, 50%) var(--y, 50%),
rgba(255, 255, 255, 0.2) 0%,
rgba(255, 255, 255, 0) 60%);
opacity: 0;
transition: opacity 0.5s;
pointer-events: none;
}
&:active::after {
opacity: 1;
transition: opacity 0.2s;
}
}
// 平滑滚动
html {
scroll-behavior: smooth;
}
// 触摸友好的列表项
.touch-list-item {
min-height: 56px;
padding: 12px 16px;
}
// 大型触摸目标
.large-touch-target {
min-height: 56px;
min-width: 56px;
}
// 全屏模式样式
.fullscreen-mode {
.v-app-bar {
background-color: rgba(var(--v-theme-surface-variant), 0.85) !important;
backdrop-filter: blur(10px);
}
.main-window {
padding-top: 16px;
padding-bottom: 16px;
}
}

View File

@ -163,3 +163,76 @@ $standard-accelerate: cubic-bezier(0.3, 0.0, 1.0, 1.0);
user-select: none; user-select: none;
-webkit-touch-callout: none; -webkit-touch-callout: none;
} }
// 动画过渡效果
// 网格项目过渡
.grid-enter-active,
.grid-leave-active {
transition: all 0.5s ease;
}
.grid-enter-from {
opacity: 0;
transform: translateY(20px);
}
.grid-leave-to {
opacity: 0;
transform: translateY(-20px);
}
// 列表项目过渡
.v-list-enter-active,
.v-list-leave-active {
transition: all 0.3s ease;
}
.v-list-enter-from {
opacity: 0;
transform: translateX(-20px);
}
.v-list-leave-to {
opacity: 0;
transform: translateX(20px);
}
// 页面过渡
.page-enter-active,
.page-leave-active {
transition: opacity 0.3s, transform 0.3s;
}
.page-enter-from {
opacity: 0;
transform: translateY(20px);
}
.page-leave-to {
opacity: 0;
transform: translateY(-20px);
}
// 淡入淡出
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
// 缩放过渡
.scale-enter-active,
.scale-leave-active {
transition: all 0.3s;
}
.scale-enter-from,
.scale-leave-to {
opacity: 0;
transform: scale(0.9);
}

View File

@ -102,6 +102,16 @@ const settingsDefinitions = {
default: true, default: true,
description: "是否显示全屏按钮", description: "是否显示全屏按钮",
}, },
"display.cardHoverEffect": {
type: "boolean",
default: true,
description: "是否启用卡片悬浮效果",
},
"display.enhancedTouchMode": {
type: "boolean",
default: true,
description: "是否启用增强触摸模式(更大的触摸目标)",
},
// 服务器设置(合并了数据提供者设置) // 服务器设置(合并了数据提供者设置)
"server.domain": { "server.domain": {