feat(front): 前台默认浅色模式并新增居中投稿按钮与实时空闲档期

- 主题: useTheme 默认 ModernLight + variables.css SSR-safe 浅值并精修组件对比度
- 首页: 插入居中投稿 CTA + 紧邻档期概览卡片(今日排期/待播歌曲/空闲档期数)
- 数据: useSongs 新增 fetchExpectedPlayDates 接入空闲档期并走 revision 轮询实时刷新
- 视觉: 移动端优先,少圆角硬边几何 + 概览卡片 3→1 列响应式
This commit is contained in:
2026-09-13 19:32:27 +08:00
parent f80abe659f
commit 92704d6637
9 changed files with 1836 additions and 1463 deletions
+56
View File
@@ -17,6 +17,7 @@ export const useSongs = () => {
const playTimes = ref<PlayTime[]>([])
const playTimeEnabled = ref(false)
const songCount = ref(0)
const expectedPlayDates = ref<any[]>([])
// 显示通知
const showNotification = (message: string, type: 'success' | 'error' | 'info' = 'info') => {
@@ -916,6 +917,56 @@ export const useSongs = () => {
}
}
// 获取期望播出日期及空闲档期
const fetchExpectedPlayDates = async (silent = false, forceRefresh = false) => {
if (!silent) {
loading.value = true
}
try {
const data = await dedup.dedupedRequest(
'expected-play-dates',
async () => {
const url = forceRefresh
? `/api/expected-play-dates?t=${Date.now()}`
: '/api/expected-play-dates'
return await $fetch(url)
},
undefined,
forceRefresh
)
if (Array.isArray(data)) {
expectedPlayDates.value = data
} else if (data && Array.isArray((data as any).data)) {
expectedPlayDates.value = (data as any).data
} else {
expectedPlayDates.value = []
}
return expectedPlayDates.value
} catch (err: any) {
if (!silent) {
error.value = err.message || '获取档期数据失败'
}
return expectedPlayDates.value
} finally {
if (!silent) {
loading.value = false
}
}
}
const totalFreeSlots = computed(() =>
expectedPlayDates.value.reduce(
(sum, d) => sum + (Array.isArray(d.freeSequences) ? d.freeSequences.length : 0),
0
)
)
const openDaysCount = computed(() =>
expectedPlayDates.value.filter((d) => !d.isFull).length
)
const expectedPlayDatesLoaded = computed(() => Array.isArray(expectedPlayDates.value))
// 初始化加载
const initialize = async () => {
await fetchPlayTimes()
@@ -932,6 +983,10 @@ export const useSongs = () => {
publicSchedules,
visibleSongs,
songCount,
expectedPlayDates,
totalFreeSlots,
openDaysCount,
expectedPlayDatesLoaded,
loading,
error,
notification,
@@ -943,6 +998,7 @@ export const useSongs = () => {
fetchPublicSchedules,
fetchPlayTimes,
fetchSongCount,
fetchExpectedPlayDates,
refreshSongsSilent,
refreshSchedulesSilent,
checkSimilarSongs,
+7 -3
View File
@@ -99,13 +99,16 @@ export function useTheme() {
const defaultTheme = computed<string>(() => {
if (siteConfigLoaded.value && siteDefaultTheme.value) {
return siteDefaultTheme.value
const v = siteDefaultTheme.value
if (v === 'System' || v === 'dark' || v.endsWith('Dark')) return 'ModernLight'
return v
}
const fromRuntime = config.public?.defaultTheme
if (typeof fromRuntime === 'string' && fromRuntime) {
if (fromRuntime === 'System' || fromRuntime === 'dark' || fromRuntime.endsWith('Dark')) return 'ModernLight'
return fromRuntime
}
return 'System'
return 'ModernLight'
})
const theme = computed<string>({
@@ -193,7 +196,8 @@ export function useTheme() {
if (stored) {
const valid = firstValidTheme(stored)
if (valid === stored || enabledThemes.value.includes(stored)) {
const effectiveStored = resolveEffectiveTheme(valid) as string
if ((valid === stored || enabledThemes.value.includes(stored)) && effectiveStored !== 'dark') {
setTheme(stored)
watchSystemTheme()
return