You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

125 lines
3.9 KiB

<script setup lang="ts">
import { request, unwrapApiBody, type ApiResponse } from '../../../utils/http/factory'
definePageMeta({ title: 'RSS' })
type Feed = { id: number; feedUrl: string; title: string | null; lastError: string | null }
type Item = { id: number; title: string | null; canonicalUrl: string; visibility: string; feedId: number }
const feeds = ref<Feed[]>([])
const items = ref<Item[]>([])
const feedUrl = ref('')
const loading = ref(true)
async function load() {
loading.value = true
try {
const [f, i] = await Promise.all([
request<ApiResponse<{ feeds: Feed[] }>>('/api/me/rss/feeds'),
request<ApiResponse<{ items: Item[] }>>('/api/me/rss/items'),
])
feeds.value = unwrapApiBody(f).feeds
items.value = unwrapApiBody(i).items
} finally {
loading.value = false
}
}
onMounted(load)
async function addFeed() {
await request('/api/me/rss/feeds', { method: 'POST', body: { feedUrl: feedUrl.value } })
feedUrl.value = ''
await load()
}
async function syncAll() {
await request('/api/me/rss/sync', { method: 'POST', body: {} })
await load()
}
async function removeFeed(id: number) {
await request(`/api/me/rss/feeds/${id}`, { method: 'DELETE' })
await load()
}
async function setItemVis(id: number, visibility: string) {
await request(`/api/me/rss/items/${id}`, { method: 'PATCH', body: { visibility } })
await load()
}
</script>
<template>
<UContainer class="py-8 space-y-8 max-w-4xl">
<h1 class="text-2xl font-semibold">
RSS 收件箱
</h1>
<UCard>
<div class="flex flex-col sm:flex-row gap-2">
<UInput v-model="feedUrl" placeholder="https://example.com/feed.xml" class="flex-1" />
<UButton @click="addFeed">
添加订阅
</UButton>
<UButton color="neutral" variant="outline" @click="syncAll">
全部同步
</UButton>
</div>
</UCard>
<div v-if="loading" class="text-muted">
加载中…
</div>
<div v-else class="grid md:grid-cols-3 gap-6">
<UCard class="md:col-span-1">
<template #header>
订阅源(仅自己可见)
</template>
<UEmpty v-if="!feeds.length" title="暂无订阅" />
<ul v-else class="space-y-2 text-sm">
<li v-for="f in feeds" :key="f.id" class="border-b border-default pb-2">
<div class="font-medium truncate" :title="f.feedUrl">
{{ f.title || f.feedUrl }}
</div>
<div v-if="f.lastError" class="text-error text-xs">
{{ f.lastError }}
</div>
<UButton size="xs" color="error" variant="ghost" @click="removeFeed(f.id)">
删除
</UButton>
</li>
</ul>
</UCard>
<UCard class="md:col-span-2">
<template #header>
条目
</template>
<UEmpty v-if="!items.length" title="暂无条目" description="添加订阅并点击同步" />
<ul v-else class="space-y-3 text-sm">
<li v-for="it in items" :key="it.id" class="border border-default rounded-lg p-3">
<a
:href="it.canonicalUrl"
target="_blank"
rel="noopener noreferrer"
class="text-primary font-medium hover:underline"
>{{ it.title || '未命名' }}</a>
<div class="text-xs text-muted mt-1">
{{ it.visibility }}
</div>
<div class="flex gap-1 mt-2">
<UButton size="xs" variant="soft" @click="setItemVis(it.id, 'private')">
私密
</UButton>
<UButton size="xs" variant="soft" @click="setItemVis(it.id, 'public')">
公开
</UButton>
<UButton size="xs" variant="soft" @click="setItemVis(it.id, 'unlisted')">
仅链接
</UButton>
</div>
</li>
</ul>
</UCard>
</div>
</UContainer>
</template>