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.
151 lines
4.3 KiB
151 lines
4.3 KiB
<script setup lang="ts">
|
|
import { useAuthSession } from '../../../composables/useAuthSession'
|
|
|
|
usePageTitle('我的文章')
|
|
|
|
type Row = { id: number; title: string; slug: string; visibility: string }
|
|
type ViewMode = 'list' | 'card'
|
|
|
|
const posts = ref<Row[]>([])
|
|
const loading = ref(true)
|
|
const viewMode = ref<ViewMode>('card')
|
|
const { user } = useAuthSession()
|
|
const { fetchData } = useClientApi()
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
try {
|
|
const { posts: list } = await fetchData<{ posts: Row[] }>('/api/me/posts')
|
|
posts.value = list
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
void load()
|
|
})
|
|
|
|
function postDetailHref(slug: string, visibility: string) {
|
|
const ps = user.value?.publicSlug
|
|
if (!ps || visibility !== 'public') {
|
|
return ''
|
|
}
|
|
return `/@${ps}/posts/${encodeURIComponent(slug)}`
|
|
}
|
|
|
|
function visibilityLabel(visibility: string) {
|
|
if (visibility === 'public') {
|
|
return '公开'
|
|
}
|
|
if (visibility === 'unlisted') {
|
|
return '仅链接'
|
|
}
|
|
return '私密'
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UContainer class="py-8 space-y-4 max-w-6xl">
|
|
<div class="flex flex-wrap justify-between items-center gap-3">
|
|
<h1 class="text-2xl font-semibold">
|
|
文章
|
|
</h1>
|
|
<div class="flex items-center gap-2">
|
|
<UButtonGroup size="sm" class="rounded-lg border border-default p-0.5 bg-elevated/40">
|
|
<UButton
|
|
:variant="viewMode === 'list' ? 'soft' : 'ghost'"
|
|
color="neutral"
|
|
icon="i-lucide-list"
|
|
:class="viewMode === 'list' ? 'shadow-sm' : 'text-muted'"
|
|
@click="viewMode = 'list'"
|
|
>
|
|
列表
|
|
</UButton>
|
|
<UButton
|
|
:variant="viewMode === 'card' ? 'soft' : 'ghost'"
|
|
color="neutral"
|
|
icon="i-lucide-layout-grid"
|
|
:class="viewMode === 'card' ? 'shadow-sm' : 'text-muted'"
|
|
@click="viewMode = 'card'"
|
|
>
|
|
卡片
|
|
</UButton>
|
|
</UButtonGroup>
|
|
<UButton to="/me/posts/new">
|
|
新建
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="loading" class="text-muted">
|
|
加载中…
|
|
</div>
|
|
<UEmpty v-else-if="!posts.length" title="暂无文章" description="创建第一篇 Markdown 文章" />
|
|
<ul v-else-if="viewMode === 'list'" class="space-y-2">
|
|
<li
|
|
v-for="p in posts"
|
|
:key="p.id"
|
|
class="flex flex-wrap justify-between items-center gap-3 border border-default rounded-lg p-3"
|
|
>
|
|
<div class="min-w-0 flex-1">
|
|
<div class="font-medium truncate">
|
|
{{ p.title }}
|
|
</div>
|
|
<div class="text-xs text-muted">
|
|
/{{ p.slug }} · {{ visibilityLabel(p.visibility) }}
|
|
</div>
|
|
</div>
|
|
<div class="flex flex-wrap gap-1 justify-end">
|
|
<UButton
|
|
v-if="postDetailHref(p.slug, p.visibility)"
|
|
:to="postDetailHref(p.slug, p.visibility)"
|
|
size="xs"
|
|
variant="soft"
|
|
color="neutral"
|
|
>
|
|
详情
|
|
</UButton>
|
|
<UButton :to="`/me/posts/${p.id}`" size="xs" variant="ghost">
|
|
编辑
|
|
</UButton>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
<div v-else class="grid gap-4 sm:grid-cols-2 xl:grid-cols-3">
|
|
<UCard
|
|
v-for="p in posts"
|
|
:key="p.id"
|
|
:ui="{ body: 'p-4 space-y-3', footer: 'px-4 py-3 border-t border-default' }"
|
|
>
|
|
<div class="space-y-2">
|
|
<div class="font-medium line-clamp-2 min-h-12">
|
|
{{ p.title }}
|
|
</div>
|
|
<div class="text-xs text-muted break-all">
|
|
/{{ p.slug }}
|
|
</div>
|
|
<UBadge size="sm" color="neutral" variant="soft">
|
|
{{ visibilityLabel(p.visibility) }}
|
|
</UBadge>
|
|
</div>
|
|
<template #footer>
|
|
<div class="flex flex-wrap gap-2 justify-end">
|
|
<UButton
|
|
v-if="postDetailHref(p.slug, p.visibility)"
|
|
:to="postDetailHref(p.slug, p.visibility)"
|
|
size="xs"
|
|
variant="soft"
|
|
color="neutral"
|
|
>
|
|
详情
|
|
</UButton>
|
|
<UButton :to="`/me/posts/${p.id}`" size="xs" variant="ghost">
|
|
编辑
|
|
</UButton>
|
|
</div>
|
|
</template>
|
|
</UCard>
|
|
</div>
|
|
</UContainer>
|
|
</template>
|
|
|