feat: add login ip desc request

This commit is contained in:
engigu
2025-10-01 08:17:03 +08:00
parent baed8b4254
commit c95f283290
3 changed files with 69 additions and 6 deletions
@@ -1,6 +1,6 @@
<script setup lang="ts">
import StatCard from '@/components/pages/dashboard/CardNum.vue'
import { DatabaseIcon, BarChartIcon, SendIcon, CheckCircleIcon, XCircleIcon } from 'lucide-vue-next'
import { InboxIcon, FileTextIcon, SendIcon, CheckCircleIcon, XCircleIcon } from 'lucide-vue-next'
// import { LineChart } from "@/components/ui/chart-line"
import { onMounted, reactive } from 'vue';
import { request } from '@/api/api';
@@ -438,14 +438,14 @@ onMounted(() => {
title="托管消息数"
:value="state.basicData.hosted_message_total_num"
description=""
:icon="BarChartIcon"
:icon="InboxIcon"
route-path="/hostedmessage"
/>
<StatCard
title="发送日志数"
:value="state.basicData.message_total_num"
description=""
:icon="DatabaseIcon"
:icon="FileTextIcon"
route-path="/sendlogs"
/>
<StatCard
@@ -1,6 +1,8 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { toast } from 'vue-sonner'
// @ts-ignore
import { request } from '@/api/api'
@@ -15,6 +17,10 @@ interface LoginLog {
const loading = ref(false)
const logs = ref<LoginLog[]>([])
const ipDialogOpen = ref(false)
const ipLoading = ref(false)
const selectedIp = ref('')
const ipInfo = ref<any>(null)
const fetchLogs = async () => {
loading.value = true
@@ -30,6 +36,23 @@ const fetchLogs = async () => {
}
onMounted(fetchLogs)
const openIpInfo = async (ip: string) => {
selectedIp.value = ip
ipDialogOpen.value = true
ipLoading.value = true
ipInfo.value = null
try {
const rsp = await fetch(`https://api.ip.sb/geoip/${encodeURIComponent(ip)}`)
if (!rsp.ok) throw new Error('请求失败')
const data = await rsp.json()
ipInfo.value = data
} catch (e) {
toast.error('获取IP信息失败')
} finally {
ipLoading.value = false
}
}
</script>
<template>
@@ -65,7 +88,9 @@ onMounted(fetchLogs)
</tr>
<tr v-for="item in logs" :key="item.id" class="border-t border-border">
<td class="px-3 py-2">{{ item.username }}</td>
<td class="px-3 py-2">{{ item.ip }}</td>
<td class="px-3 py-2">
<button class="text-blue-600 dark:text-blue-400 hover:underline" @click="openIpInfo(item.ip)">{{ item.ip }}</button>
</td>
<td class="px-3 py-2 truncate max-w-[220px] sm:max-w-[420px]" :title="item.ua">{{ item.ua }}</td>
<td class="px-3 py-2">{{ item.created_on }}</td>
</tr>
@@ -75,6 +100,44 @@ onMounted(fetchLogs)
</div>
</CardContent>
</Card>
<Dialog :open="ipDialogOpen" @update:open="val => (ipDialogOpen = val)">
<DialogContent class="w-[90vw] max-w-[90vw] sm:max-w-lg max-h-[80vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>IP 信息 - {{ selectedIp }}</DialogTitle>
</DialogHeader>
<div v-if="ipLoading" class="text-sm text-muted-foreground">加载中...</div>
<div v-else class="space-y-3 text-sm">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-2">
<div class="text-muted-foreground">organization</div>
<div class="break-all">{{ ipInfo?.organization ?? '-' }}</div>
<div class="text-muted-foreground">timezone</div>
<div class="break-all">{{ ipInfo?.timezone ?? '-' }}</div>
<div class="text-muted-foreground">ip</div>
<div class="break-all">{{ ipInfo?.ip ?? selectedIp }}</div>
<div class="text-muted-foreground">offset</div>
<div class="break-all">{{ ipInfo?.offset ?? '-' }}</div>
<div class="text-muted-foreground">isp</div>
<div class="break-all">{{ ipInfo?.isp ?? '-' }}</div>
<div class="text-muted-foreground">continent_code</div>
<div class="break-all">{{ ipInfo?.continent_code ?? '-' }}</div>
<div class="text-muted-foreground">asn_organization</div>
<div class="break-all">{{ ipInfo?.asn_organization ?? '-' }}</div>
<div class="text-muted-foreground">country</div>
<div class="break-all">{{ ipInfo?.country ?? '-' }}</div>
<div class="text-muted-foreground">asn</div>
<div class="break-all">{{ ipInfo?.asn ?? '-' }}</div>
<div class="text-muted-foreground">country_code</div>
<div class="break-all">{{ ipInfo?.country_code ?? '-' }}</div>
<div class="text-muted-foreground">latitude</div>
<div class="break-all">{{ ipInfo?.latitude ?? '-' }}</div>
<div class="text-muted-foreground">longitude</div>
<div class="break-all">{{ ipInfo?.longitude ?? '-' }}</div>
</div>
<div class="text-xs text-muted-foreground mt-2">数据来源<a href="https://api.ip.sb/geoip/185.222.222.222" target="_blank" rel="noreferrer" class="underline">api.ip.sb</a></div>
</div>
</DialogContent>
</Dialog>
</template>
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { KeyIcon, TrashIcon, SettingsIcon, InfoIcon } from 'lucide-vue-next'
import { KeyIcon, TrashIcon, SettingsIcon, InfoIcon, HistoryIcon } from 'lucide-vue-next'
// 定义props
interface Props {
@@ -19,7 +19,7 @@ defineEmits<Emits>()
const settingsMenu = [
{ id: 'password', name: '重置密码', icon: KeyIcon },
{ id: 'logs', name: '日志清理', icon: TrashIcon },
{ id: 'loginlogs', name: '登录日志', icon: InfoIcon },
{ id: 'loginlogs', name: '登录日志', icon: HistoryIcon },
{ id: 'site', name: '站点设置', icon: SettingsIcon },
{ id: 'about', name: '站点关于', icon: InfoIcon }
]