1
This commit is contained in:
@@ -13,9 +13,49 @@
|
|||||||
<p class="text-red-600 mb-4">{{ error }}</p>
|
<p class="text-red-600 mb-4">{{ error }}</p>
|
||||||
<el-button @click="loadSystemStatistics" class="mt-4">重试</el-button>
|
<el-button @click="loadSystemStatistics" class="mt-4">重试</el-button>
|
||||||
</div>
|
</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">
|
<div class="grid grid-cols-2 lg:grid-cols-5 gap-3 mb-4">
|
||||||
<!-- 总用户数卡片 -->
|
<!-- 总用户数卡片 -->
|
||||||
@@ -467,6 +507,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { apiCallApi } from '@/api'
|
||||||
import {
|
import {
|
||||||
adminGetApiDomainStatistics,
|
adminGetApiDomainStatistics,
|
||||||
adminGetApiPopularityRanking,
|
adminGetApiPopularityRanking,
|
||||||
@@ -525,6 +566,11 @@ const apiPopularityData = ref([])
|
|||||||
// 今日认证企业数据
|
// 今日认证企业数据
|
||||||
const todayCertifiedEnterprises = ref([])
|
const todayCertifiedEnterprises = ref([])
|
||||||
|
|
||||||
|
// 最近API调用记录(队列,最多10条)
|
||||||
|
const recentApiCalls = ref([])
|
||||||
|
const recentApiCallsLoading = ref(false)
|
||||||
|
const MAX_RECENT_CALLS = 10
|
||||||
|
|
||||||
// 快捷选择配置
|
// 快捷选择配置
|
||||||
const shortcuts = [
|
const shortcuts = [
|
||||||
{
|
{
|
||||||
@@ -657,13 +703,26 @@ const formatTime = (() => {
|
|||||||
// 初始化默认时间范围
|
// 初始化默认时间范围
|
||||||
const initDefaultDateRange = () => {
|
const initDefaultDateRange = () => {
|
||||||
const today = new Date()
|
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 = [
|
const defaultRange = [
|
||||||
thirtyDaysAgo.toISOString().split('T')[0],
|
formatDateLocal(firstDayOfMonth), // 当月1日
|
||||||
today.toISOString().split('T')[0]
|
formatDateLocal(today) // 今天
|
||||||
]
|
]
|
||||||
|
|
||||||
// 为所有图表设置默认时间范围(近一个月)
|
// 为所有图表设置默认时间范围(当月1日到今天)
|
||||||
userDateRange.value = [...defaultRange]
|
userDateRange.value = [...defaultRange]
|
||||||
apiCallsDateRange.value = [...defaultRange]
|
apiCallsDateRange.value = [...defaultRange]
|
||||||
consumptionDateRange.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(() => {
|
onMounted(() => {
|
||||||
initDefaultDateRange()
|
initDefaultDateRange()
|
||||||
loadSystemStatistics()
|
loadSystemStatistics()
|
||||||
|
// 初始化加载最近10条API调用记录
|
||||||
|
initRecentApiCalls()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 组件卸载时清理
|
// 组件卸载时清理
|
||||||
|
|||||||
Reference in New Issue
Block a user