Browse Source

feat(admin-ui): show per-user stats and public profile actions

Made-with: Cursor
main
npmrun 5 hours ago
parent
commit
0a00a11475
  1. 60
      app/pages/me/admin/users/index.vue

60
app/pages/me/admin/users/index.vue

@ -1,18 +1,39 @@
<script setup lang="ts">
import { request, unwrapApiBody, type ApiResponse } from '../../../../utils/http/factory'
import { useAuthSession } from '../../../../composables/useAuthSession'
import { buildPublicProfileAbsoluteUrl } from '../../../../utils/public-profile-url'
definePageMeta({ title: '用户管理' })
const { user, refresh } = useAuthSession()
const toast = useToast()
const rows = ref<
{ id: number; username: string; role: string; status: string; publicSlug: string | null }[]
{
id: number
username: string
role: string
status: string
publicSlug: string | null
postCount: number
timelineEventCount: number
rssFeedCount: number
}[]
>([])
const loading = ref(true)
const form = reactive({ username: '', password: '', email: '' })
const creating = ref(false)
async function copyPublicUrl(publicSlug: string) {
const href = buildPublicProfileAbsoluteUrl(window.location.origin, publicSlug)
try {
await navigator.clipboard.writeText(href)
toast.add({ title: '已复制公开页链接', color: 'success' })
} catch {
toast.add({ title: '复制失败', color: 'error' })
}
}
async function ensureAdmin() {
await refresh(true)
if (user.value?.role !== 'admin') {
@ -103,6 +124,18 @@ async function setStatus(id: number, status: 'active' | 'disabled') {
<th class="pb-2">
状态
</th>
<th class="pb-2">
文章
</th>
<th class="pb-2">
时间线
</th>
<th class="pb-2">
RSS
</th>
<th class="pb-2">
公开页
</th>
<th class="pb-2" />
</tr>
</thead>
@ -120,6 +153,31 @@ async function setStatus(id: number, status: 'active' | 'disabled') {
<td class="py-2">
{{ u.status }}
</td>
<td class="py-2 tabular-nums">
{{ u.postCount }}
</td>
<td class="py-2 tabular-nums">
{{ u.timelineEventCount }}
</td>
<td class="py-2 tabular-nums">
{{ u.rssFeedCount }}
</td>
<td class="py-2">
<template v-if="u.publicSlug?.trim()">
<div class="flex items-center gap-1 flex-wrap">
<NuxtLink
class="text-primary hover:underline"
:to="`/@${u.publicSlug.trim()}`"
>
@{{ u.publicSlug.trim() }}
</NuxtLink>
<UButton size="xs" variant="soft" @click="copyPublicUrl(u.publicSlug.trim())">
复制链接
</UButton>
</div>
</template>
<span v-else class="text-muted"></span>
</td>
<td class="py-2 text-right">
<UButton
v-if="u.status === 'active'"

Loading…
Cancel
Save