1
This commit is contained in:
@@ -13,9 +13,49 @@
|
||||
<p class="text-red-600 mb-4">{{ error }}</p>
|
||||
<el-button @click="loadSystemStatistics" class="mt-4">重试</el-button>
|
||||
</div>
|
||||
<!-- 最近API调用记录 -->
|
||||
<div v-if="!loading && !error" class="mb-3">
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-2">
|
||||
<div class="flex items-center justify-end mb-1">
|
||||
<el-icon
|
||||
:class="{ 'animate-spin': recentApiCallsLoading }"
|
||||
class="text-gray-400 cursor-pointer hover:text-gray-600"
|
||||
@click="loadRecentApiCalls"
|
||||
size="12"
|
||||
>
|
||||
<Refresh />
|
||||
</el-icon>
|
||||
</div>
|
||||
|
||||
<div v-if="recentApiCallsLoading && recentApiCalls.length === 0" class="text-center py-1">
|
||||
<el-icon size="14" class="animate-spin text-gray-400">
|
||||
<Loading />
|
||||
</el-icon>
|
||||
<p class="text-sm text-gray-500 mt-1">加载中...</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="recentApiCalls.length === 0" class="text-sm text-gray-500 py-1 text-center">
|
||||
暂无成功调用记录
|
||||
</div>
|
||||
|
||||
<div v-else class="flex flex-col gap-1.5 max-h-32 overflow-y-auto">
|
||||
<div
|
||||
v-for="(item, index) in recentApiCalls"
|
||||
:key="item.id || index"
|
||||
class="text-sm border border-gray-200 rounded px-2 py-1 hover:bg-gray-50 transition-colors text-center"
|
||||
>
|
||||
<span class="text-gray-900 font-medium">{{ getCompanyName(item) }}</span>
|
||||
<span class="text-gray-400 mx-1">·</span>
|
||||
<span class="text-blue-600">{{ getProductName(item) }}</span>
|
||||
<span class="text-gray-400 mx-1">·</span>
|
||||
<span class="text-gray-500">{{ formatRecentTime(item.created_at || item.start_at) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计内容 -->
|
||||
<div v-else-if="systemStats" class="space-y-4">
|
||||
<div v-if="systemStats" class="space-y-4">
|
||||
<!-- 概览卡片 -->
|
||||
<div class="grid grid-cols-2 lg:grid-cols-5 gap-3 mb-4">
|
||||
<!-- 总用户数卡片 -->
|
||||
@@ -467,6 +507,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { apiCallApi } from '@/api'
|
||||
import {
|
||||
adminGetApiDomainStatistics,
|
||||
adminGetApiPopularityRanking,
|
||||
@@ -525,6 +566,11 @@ const apiPopularityData = ref([])
|
||||
// 今日认证企业数据
|
||||
const todayCertifiedEnterprises = ref([])
|
||||
|
||||
// 最近API调用记录(队列,最多10条)
|
||||
const recentApiCalls = ref([])
|
||||
const recentApiCallsLoading = ref(false)
|
||||
const MAX_RECENT_CALLS = 10
|
||||
|
||||
// 快捷选择配置
|
||||
const shortcuts = [
|
||||
{
|
||||
@@ -657,13 +703,26 @@ const formatTime = (() => {
|
||||
// 初始化默认时间范围
|
||||
const initDefaultDateRange = () => {
|
||||
const today = new Date()
|
||||
const thirtyDaysAgo = new Date(today.getTime() - 30 * 24 * 60 * 60 * 1000)
|
||||
|
||||
// 获取当月第一天(1号 00:00:00)
|
||||
const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1)
|
||||
|
||||
// 格式化日期为 YYYY-MM-DD(使用本地时间,避免时区问题)
|
||||
const formatDateLocal = (date) => {
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
// 结束日期使用今天(后端会将结束日期+1天,查询时使用 < endTime,这样可以包含今天的所有数据)
|
||||
// 左区间:>= 当月1日,右区间:< 明天(即包含今天)
|
||||
const defaultRange = [
|
||||
thirtyDaysAgo.toISOString().split('T')[0],
|
||||
today.toISOString().split('T')[0]
|
||||
formatDateLocal(firstDayOfMonth), // 当月1日
|
||||
formatDateLocal(today) // 今天
|
||||
]
|
||||
|
||||
// 为所有图表设置默认时间范围(近一个月)
|
||||
// 为所有图表设置默认时间范围(当月1日到今天)
|
||||
userDateRange.value = [...defaultRange]
|
||||
apiCallsDateRange.value = [...defaultRange]
|
||||
consumptionDateRange.value = [...defaultRange]
|
||||
@@ -1303,9 +1362,133 @@ const getNoDataMessage = (periodRef) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载最近API调用记录(只显示成功的,使用队列机制,最多10条)
|
||||
const loadRecentApiCalls = async () => {
|
||||
recentApiCallsLoading.value = true
|
||||
try {
|
||||
const response = await apiCallApi.getAdminApiCalls({
|
||||
page: 1,
|
||||
page_size: 1, // 只获取最新1条
|
||||
status: 'success'
|
||||
})
|
||||
|
||||
const newItems = response?.data?.items || []
|
||||
if (newItems.length > 0) {
|
||||
const newItem = newItems[0]
|
||||
|
||||
// 检查是否已存在(根据ID判断)
|
||||
const exists = recentApiCalls.value.some(item => item.id === newItem.id)
|
||||
|
||||
if (!exists) {
|
||||
// 新数据入队(添加到前面)
|
||||
recentApiCalls.value.unshift(newItem)
|
||||
|
||||
// 如果超过最大数量,移除最旧的(队尾出队)
|
||||
if (recentApiCalls.value.length > MAX_RECENT_CALLS) {
|
||||
recentApiCalls.value = recentApiCalls.value.slice(0, MAX_RECENT_CALLS)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载最近API调用记录失败:', err)
|
||||
} finally {
|
||||
recentApiCallsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化时加载最近10条记录
|
||||
const initRecentApiCalls = async () => {
|
||||
recentApiCallsLoading.value = true
|
||||
try {
|
||||
console.log('开始加载最近API调用记录...')
|
||||
const response = await apiCallApi.getAdminApiCalls({
|
||||
page: 1,
|
||||
page_size: MAX_RECENT_CALLS,
|
||||
status: 'success'
|
||||
})
|
||||
|
||||
console.log('API调用记录响应:', response)
|
||||
console.log('响应数据:', response?.data)
|
||||
console.log('items:', response?.data?.items)
|
||||
|
||||
const items = response?.data?.items || []
|
||||
console.log('解析到的items数量:', items.length)
|
||||
|
||||
if (items.length > 0) {
|
||||
console.log('第一条记录:', items[0])
|
||||
}
|
||||
|
||||
recentApiCalls.value = items.slice(0, MAX_RECENT_CALLS)
|
||||
console.log('最终设置的recentApiCalls:', recentApiCalls.value)
|
||||
} catch (err) {
|
||||
console.error('初始化最近API调用记录失败:', err)
|
||||
console.error('错误详情:', err.response || err.message)
|
||||
recentApiCalls.value = []
|
||||
} finally {
|
||||
recentApiCallsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 获取企业名称
|
||||
const getCompanyName = (item) => {
|
||||
if (item?.company_name) {
|
||||
return item.company_name
|
||||
}
|
||||
if (item?.user?.company_name) {
|
||||
return item.user.company_name
|
||||
}
|
||||
return '未知企业'
|
||||
}
|
||||
|
||||
// 获取产品名称
|
||||
const getProductName = (item) => {
|
||||
if (item?.product_name) {
|
||||
return item.product_name
|
||||
}
|
||||
return '未知接口'
|
||||
}
|
||||
|
||||
// 格式化最近时间(显示相对时间或具体时间)
|
||||
const formatRecentTime = (timeStr) => {
|
||||
if (!timeStr) return '-'
|
||||
|
||||
try {
|
||||
const time = new Date(timeStr)
|
||||
const now = new Date()
|
||||
const diff = now - time // 毫秒差
|
||||
|
||||
// 小于1分钟显示"刚刚"
|
||||
if (diff < 60000) {
|
||||
return '刚刚'
|
||||
}
|
||||
// 小于1小时显示分钟数
|
||||
if (diff < 3600000) {
|
||||
const minutes = Math.floor(diff / 60000)
|
||||
return `${minutes}分钟前`
|
||||
}
|
||||
// 小于24小时显示小时数
|
||||
if (diff < 86400000) {
|
||||
const hours = Math.floor(diff / 3600000)
|
||||
return `${hours}小时前`
|
||||
}
|
||||
// 大于24小时显示具体日期时间
|
||||
return time.toLocaleString('zh-CN', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
} catch (e) {
|
||||
return timeStr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
initDefaultDateRange()
|
||||
loadSystemStatistics()
|
||||
// 初始化加载最近10条API调用记录
|
||||
initRecentApiCalls()
|
||||
})
|
||||
|
||||
// 组件卸载时清理
|
||||
|
||||
Reference in New Issue
Block a user