Files
Henry_Du 4ce4649aaf
Build VoiceHub FPK / build-fpk (push) Failing after 56m5s
Build and Push Docker Image / build-and-push (push) Canceled after 1h9m35s
初次提交
2026-09-06 08:19:02 +08:00

74 lines
2.3 KiB
Vue

<template>
<div class="min-h-screen bg-[#0a0a0a] text-zinc-100">
<div class="max-w-4xl mx-auto px-4 py-10 space-y-8">
<div class="flex items-end justify-between gap-4">
<div>
<h1 class="text-3xl font-black tracking-tight">更新日志</h1>
<p class="text-sm text-zinc-500 mt-2">记录每次更新的内容与说明</p>
</div>
<NuxtLink to="/" class="text-sm text-blue-400 hover:text-blue-300 font-semibold">
返回首页
</NuxtLink>
</div>
<div v-if="loading" class="py-10 text-center text-zinc-500">加载中...</div>
<div v-else-if="entries.length === 0" class="py-10 text-center text-zinc-500">
暂无已发布更新日志
</div>
<div v-else class="space-y-5">
<section
v-for="item in entries"
:key="item.id"
class="bg-zinc-900/40 border border-zinc-800 rounded-2xl p-6"
>
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
<div class="flex items-center gap-3">
<span class="px-2.5 py-1 rounded-lg bg-blue-500/10 text-blue-300 text-xs font-black">
v{{ item.version }}
</span>
<h2 class="text-lg font-black text-zinc-100">{{ item.title }}</h2>
</div>
<p class="text-xs text-zinc-500 font-semibold">
{{ formatDate(item.publishedAt || item.createdAt) }}
</p>
</div>
<pre class="mt-4 whitespace-pre-wrap text-sm text-zinc-300 leading-relaxed">{{
item.content
}}</pre>
</section>
</div>
</div>
<SiteFooter />
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
const loading = ref(true)
const entries = ref([])
const formatDate = (value) => {
if (!value) return ''
const d = new Date(value)
if (Number.isNaN(d.getTime())) return ''
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
const load = async () => {
try {
loading.value = true
const data = await $fetch('/api/changelog', { query: { t: Date.now() } })
entries.value = Array.isArray(data) ? data : []
} finally {
loading.value = false
}
}
onMounted(load)
</script>