Browse Source

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

Made-with: Cursor
main
npmrun 8 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"> <script setup lang="ts">
import { request, unwrapApiBody, type ApiResponse } from '../../../../utils/http/factory' import { request, unwrapApiBody, type ApiResponse } from '../../../../utils/http/factory'
import { useAuthSession } from '../../../../composables/useAuthSession' import { useAuthSession } from '../../../../composables/useAuthSession'
import { buildPublicProfileAbsoluteUrl } from '../../../../utils/public-profile-url'
definePageMeta({ title: '用户管理' }) definePageMeta({ title: '用户管理' })
const { user, refresh } = useAuthSession() const { user, refresh } = useAuthSession()
const toast = useToast()
const rows = ref< 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 loading = ref(true)
const form = reactive({ username: '', password: '', email: '' }) const form = reactive({ username: '', password: '', email: '' })
const creating = ref(false) 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() { async function ensureAdmin() {
await refresh(true) await refresh(true)
if (user.value?.role !== 'admin') { if (user.value?.role !== 'admin') {
@ -103,6 +124,18 @@ async function setStatus(id: number, status: 'active' | 'disabled') {
<th class="pb-2"> <th class="pb-2">
状态 状态
</th> </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" /> <th class="pb-2" />
</tr> </tr>
</thead> </thead>
@ -120,6 +153,31 @@ async function setStatus(id: number, status: 'active' | 'disabled') {
<td class="py-2"> <td class="py-2">
{{ u.status }} {{ u.status }}
</td> </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"> <td class="py-2 text-right">
<UButton <UButton
v-if="u.status === 'active'" v-if="u.status === 'active'"

Loading…
Cancel
Save