mirror of
https://github.com/ZeroCatDev/Classworks.git
synced 2025-12-07 21:13:11 +00:00
feat: 添加作业内容复制到今天的功能,优化浮动工具栏
This commit is contained in:
parent
3182699a78
commit
8f7b3db552
@ -1,81 +1,96 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-slide-y-transition>
|
<div class="floating-toolbar-container">
|
||||||
<v-card
|
<v-slide-y-transition>
|
||||||
:class="{ 'toolbar-expanded': isExpanded }"
|
<v-card
|
||||||
class="floating-toolbar"
|
:class="{ 'toolbar-expanded': isExpanded }"
|
||||||
elevation="4"
|
class="floating-toolbar"
|
||||||
rounded="xl"
|
elevation="4"
|
||||||
>
|
rounded="xl"
|
||||||
|
>
|
||||||
|
<v-btn-group class="toolbar-buttons" variant="text">
|
||||||
|
<v-btn
|
||||||
|
v-ripple
|
||||||
|
:title="'查看昨天'"
|
||||||
|
class="toolbar-btn"
|
||||||
|
icon="mdi-chevron-left"
|
||||||
|
variant="text"
|
||||||
|
@click="$emit('prev-day')"
|
||||||
|
/>
|
||||||
|
<v-btn
|
||||||
|
v-ripple
|
||||||
|
:title="'缩小字体'"
|
||||||
|
class="toolbar-btn"
|
||||||
|
icon="mdi-format-font-size-decrease"
|
||||||
|
variant="text"
|
||||||
|
@click="$emit('zoom', 'out')"
|
||||||
|
/>
|
||||||
|
<v-btn
|
||||||
|
v-ripple
|
||||||
|
:title="'放大字体'"
|
||||||
|
class="toolbar-btn"
|
||||||
|
icon="mdi-format-font-size-increase"
|
||||||
|
variant="text"
|
||||||
|
@click="$emit('zoom', 'up')"
|
||||||
|
/>
|
||||||
|
<v-menu :close-on-content-click="false" location="top">
|
||||||
|
<template #activator="{ props }">
|
||||||
|
<v-btn
|
||||||
|
v-ripple
|
||||||
|
:title="'选择日期'"
|
||||||
|
class="toolbar-btn"
|
||||||
|
icon="mdi-calendar"
|
||||||
|
v-bind="props"
|
||||||
|
variant="text"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<v-card border class="date-picker-card">
|
||||||
|
<v-date-picker
|
||||||
|
:model-value="selectedDate"
|
||||||
|
color="primary"
|
||||||
|
@update:model-value="handleDateSelect"
|
||||||
|
/>
|
||||||
|
</v-card>
|
||||||
|
</v-menu>
|
||||||
|
<v-btn
|
||||||
|
v-ripple
|
||||||
|
:loading="loading"
|
||||||
|
:title="'刷新数据'"
|
||||||
|
class="toolbar-btn"
|
||||||
|
icon="mdi-refresh"
|
||||||
|
variant="text"
|
||||||
|
@click="$emit('refresh')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-btn
|
||||||
|
v-if="!isToday"
|
||||||
|
v-ripple
|
||||||
|
:title="'查看明天'"
|
||||||
|
class="toolbar-btn"
|
||||||
|
icon="mdi-chevron-right"
|
||||||
|
variant="text"
|
||||||
|
@click="$emit('next-day')"
|
||||||
|
/>
|
||||||
|
</v-btn-group>
|
||||||
|
</v-card>
|
||||||
|
</v-slide-y-transition>
|
||||||
|
|
||||||
<v-btn-group class="toolbar-buttons" variant="text">
|
<!-- Side Action Button -->
|
||||||
<v-btn
|
<v-slide-x-reverse-transition>
|
||||||
v-ripple
|
<v-btn
|
||||||
:title="'查看昨天'"
|
v-if="!isToday"
|
||||||
class="toolbar-btn"
|
:loading="copyToTodayLoading"
|
||||||
icon="mdi-chevron-left"
|
:disabled="copyToTodayLoading"
|
||||||
variant="text"
|
class="side-action-btn"
|
||||||
@click="$emit('prev-day')"
|
color="primary"
|
||||||
/>
|
elevation="4"
|
||||||
<v-btn
|
prepend-icon="mdi-content-copy"
|
||||||
v-ripple
|
rounded="xl"
|
||||||
:title="'缩小字体'"
|
size="large"
|
||||||
class="toolbar-btn"
|
text="复制作业内容到今天"
|
||||||
icon="mdi-format-font-size-decrease"
|
@click="$emit('copy-to-today')"
|
||||||
variant="text"
|
>复制到今天</v-btn>
|
||||||
@click="$emit('zoom', 'out')"
|
</v-slide-x-reverse-transition>
|
||||||
/>
|
</div>
|
||||||
<v-btn
|
|
||||||
v-ripple
|
|
||||||
:title="'放大字体'"
|
|
||||||
class="toolbar-btn"
|
|
||||||
icon="mdi-format-font-size-increase"
|
|
||||||
variant="text"
|
|
||||||
@click="$emit('zoom', 'up')"
|
|
||||||
/>
|
|
||||||
<v-menu :close-on-content-click="false" location="top">
|
|
||||||
<template #activator="{ props }">
|
|
||||||
<v-btn
|
|
||||||
v-ripple
|
|
||||||
:title="'选择日期'"
|
|
||||||
class="toolbar-btn"
|
|
||||||
icon="mdi-calendar"
|
|
||||||
v-bind="props"
|
|
||||||
variant="text"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<v-card border class="date-picker-card">
|
|
||||||
<v-date-picker
|
|
||||||
:model-value="selectedDate"
|
|
||||||
color="primary"
|
|
||||||
@update:model-value="handleDateSelect"
|
|
||||||
/>
|
|
||||||
</v-card>
|
|
||||||
</v-menu>
|
|
||||||
<v-btn
|
|
||||||
v-ripple
|
|
||||||
:loading="loading"
|
|
||||||
:title="'刷新数据'"
|
|
||||||
class="toolbar-btn"
|
|
||||||
icon="mdi-refresh"
|
|
||||||
variant="text"
|
|
||||||
@click="$emit('refresh')"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<v-btn
|
|
||||||
v-if="!isToday"
|
|
||||||
v-ripple
|
|
||||||
:title="'查看明天'"
|
|
||||||
class="toolbar-btn"
|
|
||||||
icon="mdi-chevron-right"
|
|
||||||
variant="text"
|
|
||||||
@click="$emit('next-day')"
|
|
||||||
/>
|
|
||||||
</v-btn-group>
|
|
||||||
|
|
||||||
|
|
||||||
</v-card>
|
|
||||||
</v-slide-y-transition>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@ -98,6 +113,10 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
copyToTodayLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -113,12 +132,24 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.floating-toolbar {
|
.floating-toolbar-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.floating-toolbar {
|
||||||
|
position: absolute;
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
z-index: 100;
|
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
background: rgba(255, 255, 255, 0.7) !important;
|
background: rgba(255, 255, 255, 0.7) !important;
|
||||||
backdrop-filter: blur(12px);
|
backdrop-filter: blur(12px);
|
||||||
@ -132,7 +163,9 @@ export default {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 0px;
|
padding: 0 4px;
|
||||||
|
pointer-events: auto;
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-toolbar:hover {
|
.floating-toolbar:hover {
|
||||||
@ -140,6 +173,15 @@ export default {
|
|||||||
background: rgba(255, 255, 255, 0.8) !important;
|
background: rgba(255, 255, 255, 0.8) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toolbar-buttons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-btn {
|
||||||
|
margin: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.toolbar-btn:hover {
|
.toolbar-btn:hover {
|
||||||
background: rgba(255, 255, 255, 0.3) !important;
|
background: rgba(255, 255, 255, 0.3) !important;
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
@ -149,6 +191,17 @@ export default {
|
|||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.side-action-btn {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
pointer-events: auto;
|
||||||
|
z-index: 101;
|
||||||
|
background: rgba(255, 255, 255, 0.9) !important;
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
.date-picker-card {
|
.date-picker-card {
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -161,7 +214,8 @@ export default {
|
|||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.floating-toolbar {
|
.floating-toolbar {
|
||||||
bottom: 16px;
|
bottom: 16px;
|
||||||
width: 95%;
|
width: auto;
|
||||||
|
max-width: 95%;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,10 +227,12 @@ export default {
|
|||||||
|
|
||||||
.toolbar-btn {
|
.toolbar-btn {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
min-width: 40px; /* Ensure touch target */
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-btn {
|
.side-action-btn {
|
||||||
margin: 0 2px;
|
bottom: 80px; /* Move above toolbar on mobile */
|
||||||
|
right: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,5 +255,11 @@ export default {
|
|||||||
background: rgba(30, 30, 30, 0.9) !important;
|
background: rgba(30, 30, 30, 0.9) !important;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.side-action-btn {
|
||||||
|
background: rgba(30, 30, 30, 0.9) !important;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -207,6 +207,7 @@
|
|||||||
<floating-toolbar
|
<floating-toolbar
|
||||||
:is-today="isToday"
|
:is-today="isToday"
|
||||||
:loading="loading.download"
|
:loading="loading.download"
|
||||||
|
:copy-to-today-loading="loading.copyToToday"
|
||||||
:selected-date="state.selectedDateObj"
|
:selected-date="state.selectedDateObj"
|
||||||
:unread-count="unreadCount"
|
:unread-count="unreadCount"
|
||||||
@refresh="downloadData"
|
@refresh="downloadData"
|
||||||
@ -216,6 +217,7 @@
|
|||||||
@date-select="handleDateSelect"
|
@date-select="handleDateSelect"
|
||||||
@prev-day="navigateDay(-1)"
|
@prev-day="navigateDay(-1)"
|
||||||
@next-day="navigateDay(1)"
|
@next-day="navigateDay(1)"
|
||||||
|
@copy-to-today="copyHomeworkToToday"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 添加ICP备案悬浮组件 -->
|
<!-- 添加ICP备案悬浮组件 -->
|
||||||
@ -433,6 +435,7 @@ export default {
|
|||||||
download: false,
|
download: false,
|
||||||
upload: false,
|
upload: false,
|
||||||
students: false,
|
students: false,
|
||||||
|
copyToToday: false,
|
||||||
},
|
},
|
||||||
debouncedUpload: null,
|
debouncedUpload: null,
|
||||||
debouncedAttendanceSave: null,
|
debouncedAttendanceSave: null,
|
||||||
@ -1914,6 +1917,67 @@ export default {
|
|||||||
this.handleDateSelect(currentDate);
|
this.handleDateSelect(currentDate);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async copyHomeworkToToday() {
|
||||||
|
if (this.loading.copyToToday) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.loading.copyToToday = true;
|
||||||
|
|
||||||
|
// 1. 保存当前选中日期的作业数据
|
||||||
|
const sourceDate = this.state.dateString;
|
||||||
|
const sourceHomework = JSON.parse(JSON.stringify(this.state.boardData.homework));
|
||||||
|
|
||||||
|
// 2. 切换到今天并加载今天的数据(主要是为了获取考勤等其他数据)
|
||||||
|
const today = this.getToday();
|
||||||
|
const todayString = this.formatDate(today);
|
||||||
|
|
||||||
|
// 临时切换到今天以加载数据
|
||||||
|
this.state.dateString = todayString;
|
||||||
|
await this.downloadData();
|
||||||
|
|
||||||
|
// 3. 直接替换今天的作业数据(删除原有作业,使用源日期的作业)
|
||||||
|
// 深拷贝源日期的作业数据
|
||||||
|
const newHomework = {};
|
||||||
|
for (const key in sourceHomework) {
|
||||||
|
if (sourceHomework[key] && sourceHomework[key].content) {
|
||||||
|
// 如果是自定义卡片,保留完整结构
|
||||||
|
if (sourceHomework[key].type === 'custom') {
|
||||||
|
newHomework[key] = JSON.parse(JSON.stringify(sourceHomework[key]));
|
||||||
|
} else {
|
||||||
|
// 普通作业,只复制内容
|
||||||
|
newHomework[key] = {
|
||||||
|
content: sourceHomework[key].content
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接替换作业数据
|
||||||
|
this.state.boardData.homework = newHomework;
|
||||||
|
this.state.synced = false;
|
||||||
|
|
||||||
|
// 4. 保存到今天
|
||||||
|
await this.uploadData();
|
||||||
|
|
||||||
|
// 5. 更新视图状态为今天
|
||||||
|
this.state.selectedDate = todayString;
|
||||||
|
this.state.selectedDateObj = today;
|
||||||
|
this.state.isToday = true;
|
||||||
|
|
||||||
|
// 6. 更新URL
|
||||||
|
const url = new URL(window.location);
|
||||||
|
url.searchParams.delete('date');
|
||||||
|
window.history.pushState({}, '', url);
|
||||||
|
|
||||||
|
this.$message.success("复制成功", `已将 ${sourceDate} 的作业内容复制到今天(已替换原有作业)`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("复制作业失败:", error);
|
||||||
|
this.$message.error("复制失败", error.message || "请重试");
|
||||||
|
} finally {
|
||||||
|
this.loading.copyToToday = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 解析预配数据
|
// 解析预配数据
|
||||||
parsePreconfigData() {
|
parsePreconfigData() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user