feat: 添加一言卡片及其设置功能,支持动态内容刷新

This commit is contained in:
Sunwuyuan
2025-12-07 16:21:02 +08:00
parent 4627605178
commit 7d90e6ee33
7 changed files with 468 additions and 4 deletions
+184
View File
@@ -0,0 +1,184 @@
<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>