mirror of
https://github.com/ZeroCatDev/Classworks.git
synced 2026-02-04 16:03:10 +00:00
Compare commits
No commits in common. "1b25332a2f5aa054ba08dcfdc7fc7bc5b8123a1b" and "1831c9144d8a9e6192511a4eb797d80dd5ee87ef" have entirely different histories.
1b25332a2f
...
1831c9144d
@ -68,6 +68,7 @@
|
|||||||
/* 当被移除或隐藏时可渐隐(由应用控制) */
|
/* 当被移除或隐藏时可渐隐(由应用控制) */
|
||||||
body.app-loaded #app-loader { opacity: 0; pointer-events: none; }
|
body.app-loaded #app-loader { opacity: 0; pointer-events: none; }
|
||||||
</style>
|
</style>
|
||||||
|
<script defer src="https://umami.wuyuan.dev/script.js" data-website-id="e3f8ed7a-4db4-4081-aaf4-45396b1f479c"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 应用加载前显示的覆盖层:纯 CSS,无脚本依赖 -->
|
<!-- 应用加载前显示的覆盖层:纯 CSS,无脚本依赖 -->
|
||||||
|
|||||||
@ -1,184 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-card
|
|
||||||
class="hitokoto-card"
|
|
||||||
elevation="2"
|
|
||||||
border
|
|
||||||
rounded="xl"
|
|
||||||
:loading="loading"
|
|
||||||
height="100%"
|
|
||||||
@click="fetchSentence"
|
|
||||||
>
|
|
||||||
<v-card-text class="pa-6 d-flex flex-column justify-center" style="height: 100%">
|
|
||||||
<div class="text-h6 font-weight-medium mb-4 serif-font" style="white-space: pre-wrap; line-height: 1.6;">
|
|
||||||
{{ sentence }}
|
|
||||||
</div>
|
|
||||||
<div class="text-subtitle-2 text-medium-emphasis text-right serif-font">
|
|
||||||
<span v-if="author" class="mr-2">{{ author }}</span>
|
|
||||||
<span v-if="origin">《{{ origin }}》</span>
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
|
||||||
</v-card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { SettingsManager, watchSettings } from '@/utils/settings'
|
|
||||||
import dataProvider from '@/utils/dataProvider'
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'HitokotoCard',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
enabled: false,
|
|
||||||
refreshInterval: 60,
|
|
||||||
kvConfig: {
|
|
||||||
sources: ['hitokoto'],
|
|
||||||
sensitiveWords: []
|
|
||||||
},
|
|
||||||
sentence: '',
|
|
||||||
author: '',
|
|
||||||
origin: '',
|
|
||||||
loading: false,
|
|
||||||
timer: null,
|
|
||||||
unwatch: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
this.loadLocalSettings()
|
|
||||||
await this.loadKvSettings()
|
|
||||||
|
|
||||||
this.fetchSentence()
|
|
||||||
this.startTimer()
|
|
||||||
|
|
||||||
this.unwatch = watchSettings(() => {
|
|
||||||
this.loadLocalSettings()
|
|
||||||
this.startTimer()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
this.stopTimer()
|
|
||||||
if (this.unwatch) {
|
|
||||||
this.unwatch()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
loadLocalSettings() {
|
|
||||||
this.enabled = SettingsManager.getSetting('hitokoto.enabled')
|
|
||||||
this.refreshInterval = SettingsManager.getSetting('hitokoto.refreshInterval')
|
|
||||||
},
|
|
||||||
async loadKvSettings() {
|
|
||||||
try {
|
|
||||||
const res = await dataProvider.loadData('sentence-info')
|
|
||||||
let data = res
|
|
||||||
if (res && res.data) {
|
|
||||||
data = res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
this.kvConfig = {
|
|
||||||
sources: Array.isArray(data.sources) && data.sources.length > 0 ? data.sources : ['hitokoto'],
|
|
||||||
sensitiveWords: data.sensitiveWords ? data.sensitiveWords.split(/[,,]/).map(w => w.trim()).filter(w => w) : [],
|
|
||||||
jinrishiciToken: data.jinrishiciToken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to load sentence-info', e)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
startTimer() {
|
|
||||||
if (this.timer) clearInterval(this.timer)
|
|
||||||
if (this.refreshInterval > 0) {
|
|
||||||
this.timer = setInterval(this.fetchSentence, this.refreshInterval * 1000)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
stopTimer() {
|
|
||||||
if (this.timer) clearInterval(this.timer)
|
|
||||||
},
|
|
||||||
async fetchSentence() {
|
|
||||||
if (this.loading) return
|
|
||||||
this.loading = true
|
|
||||||
try {
|
|
||||||
// Pick random source
|
|
||||||
const sources = this.kvConfig.sources
|
|
||||||
const source = sources[Math.floor(Math.random() * sources.length)]
|
|
||||||
|
|
||||||
let data = null
|
|
||||||
let content = ''
|
|
||||||
let author = ''
|
|
||||||
let origin = ''
|
|
||||||
|
|
||||||
if (source === 'hitokoto') {
|
|
||||||
const res = await axios.get('https://v1.hitokoto.cn/')
|
|
||||||
data = res.data
|
|
||||||
content = data.hitokoto
|
|
||||||
author = data.from_who
|
|
||||||
origin = data.from
|
|
||||||
} else if (source === 'zhaoyu') {
|
|
||||||
const res = await axios.get('https://hub.saintic.com/openservice/sentence/all.json')
|
|
||||||
if (res.data.success) {
|
|
||||||
data = res.data.data
|
|
||||||
content = data.sentence || data.content || data.name
|
|
||||||
author = data.author
|
|
||||||
origin = data.name || data.origin
|
|
||||||
}
|
|
||||||
} else if (source === 'jinrishici') {
|
|
||||||
if (this.kvConfig.jinrishiciToken) {
|
|
||||||
const res = await axios.get('https://v2.jinrishici.com/sentence', {
|
|
||||||
headers: {
|
|
||||||
'X-User-Token': this.kvConfig.jinrishiciToken
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (res.data.status === 'success') {
|
|
||||||
data = res.data.data
|
|
||||||
content = data.content
|
|
||||||
author = data.origin.author
|
|
||||||
origin = data.origin.title
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Token missing, maybe retry with another source or just fail silently
|
|
||||||
// For now, let's just log it. The settings page should handle token generation.
|
|
||||||
console.warn('Jinrishici token missing. Please enable it in settings to generate a token.')
|
|
||||||
// Retry to pick another source to avoid empty card
|
|
||||||
this.loading = false
|
|
||||||
return this.fetchSentence()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (content) {
|
|
||||||
// Sensitive word check
|
|
||||||
const hasSensitiveWord = this.kvConfig.sensitiveWords.some(word => content.includes(word))
|
|
||||||
if (hasSensitiveWord) {
|
|
||||||
// Retry
|
|
||||||
this.loading = false
|
|
||||||
return this.fetchSentence()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.sentence = content
|
|
||||||
this.author = author || ''
|
|
||||||
this.origin = origin || '未知'
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to fetch sentence', e)
|
|
||||||
this.sentence = '获取失败'
|
|
||||||
this.author = ''
|
|
||||||
this.origin = ''
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.hitokoto-card {
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
.hitokoto-card:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
.serif-font {
|
|
||||||
font-family: "Noto Serif SC", "Source Han Serif SC", "Source Han Serif", source-han-serif-sc, "Songti SC", "SimSun", "Hiragino Sans GB", system-ui, serif;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,154 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<setting-group title="一言设置" icon="mdi-comment-quote">
|
|
||||||
<setting-item setting-key="hitokoto.enabled" />
|
|
||||||
<setting-item setting-key="hitokoto.refreshInterval" />
|
|
||||||
</setting-group>
|
|
||||||
|
|
||||||
<setting-group title="数据源配置" icon="mdi-cloud-sync" class="mt-4">
|
|
||||||
<div class="text-caption text-grey px-4 pt-2 pb-2">以下配置将同步到云端,对所有连接此班级的设备生效。</div>
|
|
||||||
|
|
||||||
<v-list-item>
|
|
||||||
<v-list-item-title class="mb-2">启用数据源</v-list-item-title>
|
|
||||||
<div class="d-flex flex-wrap gap-2">
|
|
||||||
<v-checkbox
|
|
||||||
v-model="kvConfig.sources"
|
|
||||||
label="一言 (Hitokoto)"
|
|
||||||
value="hitokoto"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
class="mr-4"
|
|
||||||
:disabled="loading"
|
|
||||||
@update:model-value="saveKvSettings"
|
|
||||||
/>
|
|
||||||
<v-checkbox
|
|
||||||
v-model="kvConfig.sources"
|
|
||||||
label="诏预 (Zhaoyu)"
|
|
||||||
value="zhaoyu"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
class="mr-4"
|
|
||||||
:disabled="loading"
|
|
||||||
@update:model-value="saveKvSettings"
|
|
||||||
/>
|
|
||||||
<v-checkbox
|
|
||||||
v-model="kvConfig.sources"
|
|
||||||
label="今日诗词 (Jinrishici)"
|
|
||||||
value="jinrishici"
|
|
||||||
hide-details
|
|
||||||
density="compact"
|
|
||||||
:disabled="loading"
|
|
||||||
@update:model-value="saveKvSettings"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</v-list-item>
|
|
||||||
|
|
||||||
<v-list-item v-if="kvConfig.sources.includes('jinrishici')">
|
|
||||||
<v-text-field
|
|
||||||
v-model="kvConfig.jinrishiciToken"
|
|
||||||
label="今日诗词 Token"
|
|
||||||
variant="outlined"
|
|
||||||
density="comfortable"
|
|
||||||
:disabled="loading"
|
|
||||||
hint="留空则自动获取,也可以手动输入已有 Token"
|
|
||||||
persistent-hint
|
|
||||||
class="mt-2"
|
|
||||||
@change="saveKvSettings"
|
|
||||||
/>
|
|
||||||
</v-list-item>
|
|
||||||
|
|
||||||
<v-list-item>
|
|
||||||
<v-textarea
|
|
||||||
v-model="kvConfig.sensitiveWords"
|
|
||||||
:disabled="loading"
|
|
||||||
label="敏感词过滤 (用逗号分隔)"
|
|
||||||
variant="outlined"
|
|
||||||
rows="3"
|
|
||||||
auto-grow
|
|
||||||
hide-details
|
|
||||||
class="mt-2 mb-2"
|
|
||||||
@change="saveKvSettings"
|
|
||||||
/>
|
|
||||||
</v-list-item>
|
|
||||||
|
|
||||||
<div v-if="loading" class="text-center pb-4">
|
|
||||||
<v-progress-circular indeterminate size="24" color="primary" />
|
|
||||||
<span class="ml-2 text-caption">正在同步配置...</span>
|
|
||||||
</div>
|
|
||||||
</setting-group>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import SettingGroup from './settings/SettingGroup.vue'
|
|
||||||
import SettingItem from './settings/SettingItem.vue'
|
|
||||||
import dataProvider from '@/utils/dataProvider'
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'HitokotoSettings',
|
|
||||||
components: {
|
|
||||||
SettingGroup,
|
|
||||||
SettingItem
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
kvConfig: {
|
|
||||||
sources: ['hitokoto'],
|
|
||||||
sensitiveWords: '',
|
|
||||||
jinrishiciToken: null
|
|
||||||
},
|
|
||||||
loading: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.loadKvSettings()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async loadKvSettings() {
|
|
||||||
this.loading = true
|
|
||||||
try {
|
|
||||||
const res = await dataProvider.loadData('sentence-info')
|
|
||||||
let data = res
|
|
||||||
if (res && res.data) {
|
|
||||||
data = res.data
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
this.kvConfig = {
|
|
||||||
sources: Array.isArray(data.sources) ? data.sources : ['hitokoto'],
|
|
||||||
sensitiveWords: data.sensitiveWords || '',
|
|
||||||
jinrishiciToken: data.jinrishiciToken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to load sentence-info', e)
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async saveKvSettings() {
|
|
||||||
this.loading = true
|
|
||||||
try {
|
|
||||||
// Check if jinrishici is enabled and token is missing
|
|
||||||
if (this.kvConfig.sources.includes('jinrishici') && !this.kvConfig.jinrishiciToken) {
|
|
||||||
try {
|
|
||||||
const tokenRes = await axios.get('https://v2.jinrishici.com/token')
|
|
||||||
if (tokenRes.data.status === 'success') {
|
|
||||||
this.kvConfig.jinrishiciToken = tokenRes.data.data
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to get jinrishici token', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await dataProvider.saveData('sentence-info', this.kvConfig)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to save sentence-info', e)
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -4,21 +4,15 @@
|
|||||||
<div
|
<div
|
||||||
v-for="item in sortedItems"
|
v-for="item in sortedItems"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
ref="items"
|
|
||||||
:data-key="item.key"
|
|
||||||
:style="{
|
:style="{
|
||||||
|
'grid-row-end': `span ${item.rowSpan}`,
|
||||||
order: item.order,
|
order: item.order,
|
||||||
}"
|
}"
|
||||||
class="grid-item"
|
class="grid-item"
|
||||||
>
|
>
|
||||||
<!-- 一言卡片 -->
|
|
||||||
<div v-if="item.type === 'hitokoto'" style="height: 100%">
|
|
||||||
<hitokoto-card />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 出勤卡片 -->
|
<!-- 出勤卡片 -->
|
||||||
<v-card
|
<v-card
|
||||||
v-else-if="item.type === 'attendance'"
|
v-if="item.type === 'attendance'"
|
||||||
:class="{ 'glow-highlight': highlightedCards[item.key], 'cursor-not-allowed': isEditingDisabled, 'cursor-pointer': !isEditingDisabled }"
|
:class="{ 'glow-highlight': highlightedCards[item.key], 'cursor-not-allowed': isEditingDisabled, 'cursor-pointer': !isEditingDisabled }"
|
||||||
border
|
border
|
||||||
class="glow-track"
|
class="glow-track"
|
||||||
@ -184,13 +178,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import HitokotoCard from "@/components/HitokotoCard.vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "HomeworkGrid",
|
name: "HomeworkGrid",
|
||||||
components: {
|
|
||||||
HitokotoCard,
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
sortedItems: {
|
sortedItems: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -223,76 +212,7 @@ export default {
|
|||||||
return this.$vuetify.display.mobile;
|
return this.$vuetify.display.mobile;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
|
||||||
this.resizeObserver = new ResizeObserver(() => {
|
|
||||||
this.resizeAllGridItems();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Observe the grid container for width changes
|
|
||||||
if (this.$refs.gridContainer) {
|
|
||||||
this.resizeObserver.observe(this.$refs.gridContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial resize
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.resizeAllGridItems();
|
|
||||||
// Observe all items
|
|
||||||
if (this.$refs.items) {
|
|
||||||
this.$refs.items.forEach(item => {
|
|
||||||
// Observe the content inside the grid item
|
|
||||||
if (item.firstElementChild) {
|
|
||||||
this.resizeObserver.observe(item.firstElementChild);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
updated() {
|
|
||||||
// When items change, re-observe new items
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.resizeAllGridItems();
|
|
||||||
if (this.$refs.items) {
|
|
||||||
this.$refs.items.forEach(item => {
|
|
||||||
if (item.firstElementChild) {
|
|
||||||
this.resizeObserver.observe(item.firstElementChild);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
if (this.resizeObserver) {
|
|
||||||
this.resizeObserver.disconnect();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
resizeGridItem(item) {
|
|
||||||
const grid = this.$refs.gridContainer;
|
|
||||||
if (!grid) return;
|
|
||||||
|
|
||||||
const rowHeight = parseInt(window.getComputedStyle(grid).getPropertyValue('grid-auto-rows'));
|
|
||||||
const rowGap = parseInt(window.getComputedStyle(grid).getPropertyValue('gap'));
|
|
||||||
|
|
||||||
// Find the content element (v-card or div)
|
|
||||||
const content = item.firstElementChild;
|
|
||||||
if (!content) return;
|
|
||||||
|
|
||||||
// Calculate required span
|
|
||||||
// We use scrollHeight to get the full height of content
|
|
||||||
// Add a small buffer to prevent scrollbars
|
|
||||||
const contentHeight = content.getBoundingClientRect().height;
|
|
||||||
|
|
||||||
// Formula: span = ceil((contentHeight + gap) / (rowHeight + gap))
|
|
||||||
const rowSpan = Math.ceil((contentHeight + rowGap) / (rowHeight + rowGap));
|
|
||||||
|
|
||||||
item.style.gridRowEnd = `span ${rowSpan}`;
|
|
||||||
},
|
|
||||||
resizeAllGridItems() {
|
|
||||||
const items = this.$refs.items;
|
|
||||||
if (items) {
|
|
||||||
items.forEach(item => this.resizeGridItem(item));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleCardClick(type, key) {
|
handleCardClick(type, key) {
|
||||||
if (this.isEditingDisabled) {
|
if (this.isEditingDisabled) {
|
||||||
this.$emit('disabled-click');
|
this.$emit('disabled-click');
|
||||||
|
|||||||
@ -331,7 +331,6 @@ import AttendanceSidebar from "@/components/attendance/AttendanceSidebar.vue";
|
|||||||
import AttendanceManagementDialog from "@/components/attendance/AttendanceManagementDialog.vue";
|
import AttendanceManagementDialog from "@/components/attendance/AttendanceManagementDialog.vue";
|
||||||
import HomeworkGrid from "@/components/home/HomeworkGrid.vue";
|
import HomeworkGrid from "@/components/home/HomeworkGrid.vue";
|
||||||
import HomeActions from "@/components/home/HomeActions.vue";
|
import HomeActions from "@/components/home/HomeActions.vue";
|
||||||
import HitokotoCard from "@/components/HitokotoCard.vue";
|
|
||||||
import dataProvider from "@/utils/dataProvider";
|
import dataProvider from "@/utils/dataProvider";
|
||||||
import {
|
import {
|
||||||
getSetting,
|
getSetting,
|
||||||
@ -551,46 +550,28 @@ export default {
|
|||||||
const subjectData = this.state.boardData.homework[subjectKey];
|
const subjectData = this.state.boardData.homework[subjectKey];
|
||||||
|
|
||||||
if (subjectData && subjectData.content) {
|
if (subjectData && subjectData.content) {
|
||||||
const lineCount = subjectData.content.split("\n").filter((line) => line.trim()).length;
|
|
||||||
// Estimate height in pixels: title(64) + padding(32) + lines * line-height(24) + extra
|
|
||||||
const estimatedHeight = 100 + lineCount * 24;
|
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
key: subjectKey,
|
key: subjectKey,
|
||||||
name: subjectKey,
|
name: subjectKey,
|
||||||
type: 'homework',
|
type: 'homework',
|
||||||
content: subjectData.content,
|
content: subjectData.content,
|
||||||
order: subject.order,
|
order: subject.order,
|
||||||
rowSpan: estimatedHeight, // Used for sorting only
|
rowSpan: Math.ceil((subjectData.content.split("\n").filter((line) => line.trim()).length + 1) * 0.8),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加一言卡片
|
|
||||||
if (getSetting("hitokoto.enabled")) {
|
|
||||||
items.push({
|
|
||||||
key: "hitokoto-card",
|
|
||||||
name: "一言",
|
|
||||||
type: "hitokoto",
|
|
||||||
order: 9998,
|
|
||||||
rowSpan: 150, // Default estimated height
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加自定义卡片
|
// 添加自定义卡片
|
||||||
for (const key in this.state.boardData.homework) {
|
for (const key in this.state.boardData.homework) {
|
||||||
if (key.startsWith('custom-')) {
|
if (key.startsWith('custom-')) {
|
||||||
const card = this.state.boardData.homework[key];
|
const card = this.state.boardData.homework[key];
|
||||||
const lineCount = card.content.split("\n").filter((line) => line.trim()).length;
|
|
||||||
const estimatedHeight = 100 + lineCount * 24;
|
|
||||||
|
|
||||||
items.push({
|
items.push({
|
||||||
key: key,
|
key: key,
|
||||||
name: card.name,
|
name: card.name,
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
content: card.content,
|
content: card.content,
|
||||||
order: 9999, // Put at the end
|
order: 9999, // Put at the end
|
||||||
rowSpan: estimatedHeight, // Used for sorting only
|
rowSpan: Math.ceil((card.content.split("\n").filter((line) => line.trim()).length + 1) * 0.8),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1162,16 +1143,6 @@ export default {
|
|||||||
this.state.boardData.homework[this.currentEditSubject]?.content || "";
|
this.state.boardData.homework[this.currentEditSubject]?.content || "";
|
||||||
|
|
||||||
if (content !== originalContent.trim()) {
|
if (content !== originalContent.trim()) {
|
||||||
// 如果内容为空且是自定义卡片,则删除该卡片
|
|
||||||
if (!content && this.currentEditSubject.startsWith('custom-')) {
|
|
||||||
delete this.state.boardData.homework[this.currentEditSubject];
|
|
||||||
this.state.synced = false;
|
|
||||||
if (this.autoSave) {
|
|
||||||
await this.trySave(true);
|
|
||||||
}
|
|
||||||
this.state.dialogVisible = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 如果是自定义卡片,保留其他属性
|
// 如果是自定义卡片,保留其他属性
|
||||||
if (this.state.boardData.homework[this.currentEditSubject].type === 'custom') {
|
if (this.state.boardData.homework[this.currentEditSubject].type === 'custom') {
|
||||||
this.state.boardData.homework[this.currentEditSubject].content = content;
|
this.state.boardData.homework[this.currentEditSubject].content = content;
|
||||||
|
|||||||
@ -181,10 +181,6 @@
|
|||||||
/>
|
/>
|
||||||
</v-tabs-window-item>
|
</v-tabs-window-item>
|
||||||
|
|
||||||
<v-tabs-window-item value="hitokoto">
|
|
||||||
<hitokoto-settings border />
|
|
||||||
</v-tabs-window-item>
|
|
||||||
|
|
||||||
<v-tabs-window-item value="randomPicker">
|
<v-tabs-window-item value="randomPicker">
|
||||||
<random-picker-card :is-mobile="isMobile" border/>
|
<random-picker-card :is-mobile="isMobile" border/>
|
||||||
</v-tabs-window-item>
|
</v-tabs-window-item>
|
||||||
@ -276,7 +272,6 @@ import RandomPickerCard from "@/components/settings/cards/RandomPickerCard.vue";
|
|||||||
import HomeworkTemplateCard from "@/components/settings/cards/HomeworkTemplateCard.vue";
|
import HomeworkTemplateCard from "@/components/settings/cards/HomeworkTemplateCard.vue";
|
||||||
import SubjectManagementCard from "@/components/settings/cards/SubjectManagementCard.vue";
|
import SubjectManagementCard from "@/components/settings/cards/SubjectManagementCard.vue";
|
||||||
import KvDatabaseCard from "@/components/settings/cards/KvDatabaseCard.vue";
|
import KvDatabaseCard from "@/components/settings/cards/KvDatabaseCard.vue";
|
||||||
import HitokotoSettings from "@/components/HitokotoSettings.vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Settings",
|
name: "Settings",
|
||||||
@ -298,7 +293,6 @@ export default {
|
|||||||
HomeworkTemplateCard,
|
HomeworkTemplateCard,
|
||||||
SubjectManagementCard,
|
SubjectManagementCard,
|
||||||
KvDatabaseCard,
|
KvDatabaseCard,
|
||||||
HitokotoSettings,
|
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const {mobile} = useDisplay();
|
const {mobile} = useDisplay();
|
||||||
@ -420,11 +414,6 @@ export default {
|
|||||||
icon: "mdi-theme-light-dark",
|
icon: "mdi-theme-light-dark",
|
||||||
value: "theme",
|
value: "theme",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: "一言",
|
|
||||||
icon: "mdi-comment-quote",
|
|
||||||
value: "hitokoto",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
title: "随机点名",
|
title: "随机点名",
|
||||||
|
|||||||
@ -146,8 +146,6 @@
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
grid-auto-flow: dense;
|
grid-auto-flow: dense;
|
||||||
grid-auto-rows: 1px;
|
|
||||||
align-items: start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-item {
|
.grid-item {
|
||||||
|
|||||||
@ -102,20 +102,6 @@ const settingsDefinitions = {
|
|||||||
description: "空科目的显示方式",
|
description: "空科目的显示方式",
|
||||||
icon: "mdi-card-outline",
|
icon: "mdi-card-outline",
|
||||||
},
|
},
|
||||||
|
|
||||||
// 一言设置
|
|
||||||
"hitokoto.enabled": {
|
|
||||||
type: "boolean",
|
|
||||||
default: true,
|
|
||||||
description: "启用一言",
|
|
||||||
icon: "mdi-comment-quote",
|
|
||||||
},
|
|
||||||
"hitokoto.refreshInterval": {
|
|
||||||
type: "number",
|
|
||||||
default: 300,
|
|
||||||
description: "刷新时间(秒,0为不自动刷新)",
|
|
||||||
icon: "mdi-timer-refresh",
|
|
||||||
},
|
|
||||||
"display.dynamicSort": {
|
"display.dynamicSort": {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
default: true,
|
default: true,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user