2026-03-05 10:49:46 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
|
|
|
|
|
|
|
|
// 显示的数字(用于动画)
|
|
|
|
|
|
const displayStats = ref({
|
2026-03-06 09:29:16 +08:00
|
|
|
|
cpu: 0,
|
|
|
|
|
|
memory: 0,
|
2026-03-05 10:49:46 +08:00
|
|
|
|
activeAgents: 0,
|
|
|
|
|
|
requests: 0,
|
|
|
|
|
|
agentsCalls: 0,
|
|
|
|
|
|
mcpCalls: 0,
|
|
|
|
|
|
modelRequests: 0,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 目标数字
|
|
|
|
|
|
const targetStats = {
|
2026-03-06 09:29:16 +08:00
|
|
|
|
cpu: 45,
|
|
|
|
|
|
memory: 72,
|
2026-03-05 10:49:46 +08:00
|
|
|
|
activeAgents: 3,
|
|
|
|
|
|
requests: 36,
|
|
|
|
|
|
agentsCalls: 3,
|
|
|
|
|
|
mcpCalls: 21,
|
|
|
|
|
|
modelRequests: 13,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 数字滚动动画函数
|
|
|
|
|
|
const animateNumber = (key: keyof typeof displayStats.value, target: number) => {
|
|
|
|
|
|
const duration = 2000
|
|
|
|
|
|
const startTime = Date.now()
|
|
|
|
|
|
const startValue = 0
|
|
|
|
|
|
|
|
|
|
|
|
const animate = () => {
|
|
|
|
|
|
const elapsed = Date.now() - startTime
|
|
|
|
|
|
const progress = Math.min(elapsed / duration, 1)
|
|
|
|
|
|
// 使用easeOut曲线
|
|
|
|
|
|
const eased = 1 - Math.pow(1 - progress, 3)
|
|
|
|
|
|
displayStats.value[key] = Math.floor(startValue + (target - startValue) * eased)
|
|
|
|
|
|
|
|
|
|
|
|
if (progress < 1) {
|
|
|
|
|
|
requestAnimationFrame(animate)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
displayStats.value[key] = target
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
animate()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
// 页面加载后依次动画每个数字
|
2026-03-06 09:29:16 +08:00
|
|
|
|
setTimeout(() => animateNumber('cpu', targetStats.cpu), 0)
|
|
|
|
|
|
setTimeout(() => animateNumber('memory', targetStats.memory), 200)
|
|
|
|
|
|
setTimeout(() => animateNumber('activeAgents', targetStats.activeAgents), 400)
|
2026-03-05 10:49:46 +08:00
|
|
|
|
// deployment insights 数字动画(延迟900ms,在顶部stats之后)
|
|
|
|
|
|
setTimeout(() => animateNumber('requests', targetStats.requests), 900)
|
|
|
|
|
|
setTimeout(() => animateNumber('agentsCalls', targetStats.agentsCalls), 1000)
|
|
|
|
|
|
setTimeout(() => animateNumber('mcpCalls', targetStats.mcpCalls), 1100)
|
|
|
|
|
|
setTimeout(() => animateNumber('modelRequests', targetStats.modelRequests), 1200)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Agents列表
|
|
|
|
|
|
const agents = ref([
|
|
|
|
|
|
{ name: 'template-google-adk-api', count: 1, color: 'bg-primary-yellow', status: 'success' },
|
|
|
|
|
|
{ name: 'mcp-google-adk-api', count: 1, color: 'bg-primary-cyan', status: 'error' },
|
|
|
|
|
|
{ name: 'template-openai-api', count: 1, color: 'bg-primary-purple', status: 'error' },
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
// 图表数据
|
|
|
|
|
|
const chartData = ref([
|
|
|
|
|
|
{ time: '3:02 PM', agents: 1, mcp: 2, models: 1.5 },
|
|
|
|
|
|
{ time: '3:07 PM', agents: 2, mcp: 2.5, models: 2 },
|
|
|
|
|
|
{ time: '3:12 PM', agents: 2.5, mcp: 5, models: 4 },
|
|
|
|
|
|
{ time: '3:17 PM', agents: 2.5, mcp: 3, models: 2 },
|
|
|
|
|
|
{ time: '3:22 PM', agents: 1.5, mcp: 2.5, models: 1.5 },
|
|
|
|
|
|
{ time: '3:27 PM', agents: 1.5, mcp: 3, models: 2.5 },
|
|
|
|
|
|
{ time: '3:32 PM', agents: 1, mcp: 2, models: 2.5 },
|
|
|
|
|
|
{ time: '3:37 PM', agents: 2.5, mcp: 8, models: 3 },
|
|
|
|
|
|
{ time: '3:42 PM', agents: 1, mcp: 5, models: 2.5 },
|
|
|
|
|
|
{ time: '3:47 PM', agents: 2.5, mcp: 3, models: 2 },
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
// Top 10请求
|
|
|
|
|
|
const topRequests = ref([
|
|
|
|
|
|
{ name: 'gpt-40-2024-08-12', type: 'cube', count: 7 },
|
|
|
|
|
|
{ name: 'google-maps', type: 'code', count: 4 },
|
|
|
|
|
|
{ name: 'explorer-mcp', type: 'code', count: 2 },
|
|
|
|
|
|
{ name: 'template-google-adk-api', type: 'cube', count: 4 },
|
|
|
|
|
|
{ name: 'linear-demo', type: 'cube', count: 2 },
|
|
|
|
|
|
{ name: 'cerebras-sandbox', type: 'code', count: 1 },
|
|
|
|
|
|
{ name: 'sandbox-openai', type: 'cube', count: 2 },
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
// What's new
|
|
|
|
|
|
const whatsNew = ref([
|
|
|
|
|
|
{ title: 'New framework supported: PydanticAI', desc: 'Added support for PydanticAI framework', date: '2025-04-12' },
|
|
|
|
|
|
{ title: 'New framework supported: Google ADK', desc: 'Added support for Google ADK (Agent Development Kit) framework', date: '2025-04-07' },
|
|
|
|
|
|
{ title: 'Improved Analytics Dashboard', desc: 'Enhanced real-time monitoring with faster data refresh', date: '2025-04-15' },
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
// Recent requests
|
|
|
|
|
|
const recentRequests = ref([
|
|
|
|
|
|
{ name: 'linear-demo', type: 'cube', time: '21 hours', status: 'success' },
|
|
|
|
|
|
{ name: 'myagent', type: 'robot', time: '21 hours', status: 'success' },
|
|
|
|
|
|
{ name: 'linear-demo', type: 'cube', time: '21 hours', status: 'success' },
|
|
|
|
|
|
{ name: 'gpt-40', type: 'code', time: '21 hours', status: 'success' },
|
|
|
|
|
|
{ name: 'linear-demo', type: 'cube', time: '21 hours', status: 'success' },
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
// 开关状态
|
|
|
|
|
|
const agentErrorEnabled = ref(true)
|
|
|
|
|
|
|
|
|
|
|
|
// Top requests标签切换
|
|
|
|
|
|
const topRequestsTab = ref<'general' | 'errors'>('general')
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<!-- 主内容区域 -->
|
|
|
|
|
|
<div class="p-6 min-h-screen">
|
|
|
|
|
|
<!-- 顶部导航与日期选择区 -->
|
|
|
|
|
|
<div class="flex justify-between items-center mb-6">
|
|
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
|
<i class="fa-solid fa-gauge text-gray-400"></i>
|
|
|
|
|
|
<span class="font-medium">Dashboard</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-center gap-4">
|
|
|
|
|
|
<span class="text-gray-400">Date Range</span>
|
|
|
|
|
|
<div class="flex items-center gap-2 bg-dark-600 rounded-lg px-3 py-2">
|
|
|
|
|
|
<span>10 December</span>
|
|
|
|
|
|
<span class="text-gray-400">To</span>
|
|
|
|
|
|
<span>12 December</span>
|
|
|
|
|
|
<i class="fa-solid fa-calendar text-gray-400"></i>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<button class="text-primary-orange hover:text-orange-400 transition-colors">Clear</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 卡片网格布局 -->
|
|
|
|
|
|
<div class="grid grid-cols-3 gap-6">
|
|
|
|
|
|
<!-- 第一行:3个状态卡片 -->
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<!-- CPU 占用卡片 -->
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="bg-dark-700 rounded-xl p-5">
|
|
|
|
|
|
<div class="flex justify-between items-center mb-4">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<h3 class="font-semibold text-lg">CPU Usage</h3>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="flex items-center gap-2">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<span class="text-sm text-gray-400">Live</span>
|
|
|
|
|
|
<span class="w-2 h-2 rounded-full bg-primary-success animate-pulse"></span>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<div class="text-4xl font-bold mb-2">{{ displayStats.cpu }}<span class="text-xl text-gray-400">%</span></div>
|
|
|
|
|
|
<!-- 进度条 -->
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="w-full h-2 rounded-full bg-dark-500 overflow-hidden mb-4 relative">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<div
|
|
|
|
|
|
class="absolute left-0 top-0 h-full bg-gradient-to-r from-primary-orange to-red-500 progress-bar"
|
|
|
|
|
|
:style="{ '--target-width': displayStats.cpu + '%' }"
|
|
|
|
|
|
></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 使用情况 -->
|
|
|
|
|
|
<div class="flex justify-between text-sm text-gray-400">
|
|
|
|
|
|
<span>0%</span>
|
|
|
|
|
|
<span>50%</span>
|
|
|
|
|
|
<span>100%</span>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<!-- 内存占用卡片 -->
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="bg-dark-700 rounded-xl p-5">
|
|
|
|
|
|
<div class="flex justify-between items-center mb-4">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<h3 class="font-semibold text-lg">Memory Usage</h3>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="flex items-center gap-2">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<span class="text-sm text-gray-400">Live</span>
|
|
|
|
|
|
<span class="w-2 h-2 rounded-full bg-primary-success animate-pulse"></span>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<div class="text-4xl font-bold mb-2">{{ displayStats.memory }}<span class="text-xl text-gray-400">%</span></div>
|
|
|
|
|
|
<!-- 进度条 -->
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="w-full h-2 rounded-full bg-dark-500 overflow-hidden mb-4 relative">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<div
|
|
|
|
|
|
class="absolute left-0 top-0 h-full bg-gradient-to-r from-primary-cyan to-blue-500 progress-bar"
|
|
|
|
|
|
:style="{ '--target-width': displayStats.memory + '%' }"
|
|
|
|
|
|
></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 使用情况 -->
|
|
|
|
|
|
<div class="flex justify-between text-sm text-gray-400">
|
|
|
|
|
|
<span>0%</span>
|
|
|
|
|
|
<span>50%</span>
|
|
|
|
|
|
<span>100%</span>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<!-- Active Agents 卡片 -->
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="bg-dark-700 rounded-xl p-5">
|
|
|
|
|
|
<div class="flex justify-between items-center mb-4">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<h3 class="font-semibold text-lg">Active Agents</h3>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
|
<span class="text-sm text-gray-400">Errors</span>
|
2026-03-07 13:53:53 +08:00
|
|
|
|
<!-- 使用 Element Plus 开关组件 -->
|
|
|
|
|
|
<el-switch v-model="agentErrorEnabled" class="error-switch" />
|
2026-03-05 10:49:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<div class="text-4xl font-bold mb-4">{{ displayStats.activeAgents }}</div>
|
|
|
|
|
|
<!-- 进度条 -->
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="w-full h-2 rounded-full bg-dark-500 overflow-hidden mb-4 relative">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<div class="absolute left-0 top-0 h-full bg-primary-yellow progress-bar" style="--target-width: 33%"></div>
|
|
|
|
|
|
<div class="absolute top-0 h-full bg-primary-cyan progress-bar" style="left: 33%; --target-width: 33%"></div>
|
|
|
|
|
|
<div class="absolute top-0 h-full bg-primary-purple progress-bar" style="left: 66%; --target-width: 34%"></div>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 明细列表 -->
|
|
|
|
|
|
<ul class="space-y-2">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<li v-for="agent in agents" :key="agent.name" class="flex justify-between items-center">
|
2026-03-05 10:49:46 +08:00
|
|
|
|
<div class="flex items-center gap-2">
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<span class="w-3 h-3 rounded-sm" :class="agent.color"></span>
|
|
|
|
|
|
<span class="text-sm text-gray-300">{{ agent.name }}</span>
|
|
|
|
|
|
<span v-if="agent.status === 'error'" class="bg-primary-danger text-white text-xs px-1.5 py-0.5 rounded">Error</span>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
</div>
|
2026-03-06 09:29:16 +08:00
|
|
|
|
<span class="text-sm">{{ agent.count }}</span>
|
2026-03-05 10:49:46 +08:00
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 第二行 -->
|
|
|
|
|
|
<!-- All deployment request Insights 卡片(跨2列) -->
|
|
|
|
|
|
<div class="bg-dark-700 rounded-xl p-5 col-span-2">
|
|
|
|
|
|
<h3 class="font-semibold text-lg mb-4">All deployment request Insights</h3>
|
|
|
|
|
|
<!-- 数据概览 -->
|
|
|
|
|
|
<div class="grid grid-cols-4 gap-4 mb-6">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="text-sm text-gray-400 mb-1">Requests</div>
|
|
|
|
|
|
<div class="text-2xl font-bold">{{ displayStats.requests }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="text-sm text-gray-400 mb-1">Agents calls</div>
|
|
|
|
|
|
<div class="text-2xl font-bold">{{ displayStats.agentsCalls }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="text-sm text-gray-400 mb-1">MCP servers calls</div>
|
|
|
|
|
|
<div class="text-2xl font-bold">{{ displayStats.mcpCalls }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="text-sm text-gray-400 mb-1">Models requests</div>
|
|
|
|
|
|
<div class="text-2xl font-bold">{{ displayStats.modelRequests }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 纯CSS模拟柱状图 -->
|
|
|
|
|
|
<div class="relative h-52 w-full">
|
|
|
|
|
|
<!-- 绘图区(网格线+Y轴+柱子) 预留底部20px给时间标签 -->
|
|
|
|
|
|
<div class="relative h-[calc(100%-20px)] w-full">
|
|
|
|
|
|
<!-- 横向网格线 -->
|
|
|
|
|
|
<div class="absolute left-0 top-0 w-full h-full z-0">
|
|
|
|
|
|
<div class="absolute w-full h-[1px] bg-white/[0.06] top-0"></div>
|
|
|
|
|
|
<div class="absolute w-full h-[1px] bg-white/[0.06] top-[25%]"></div>
|
|
|
|
|
|
<div class="absolute w-full h-[1px] bg-white/[0.06] top-[50%]"></div>
|
|
|
|
|
|
<div class="absolute w-full h-[1px] bg-white/[0.06] top-[75%]"></div>
|
|
|
|
|
|
<div class="absolute w-full h-[1px] bg-white/[0.06] top-[100%]"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Y轴刻度 -->
|
|
|
|
|
|
<div class="absolute left-0 top-0 h-full flex flex-col justify-between text-xs text-gray-500 z-10">
|
|
|
|
|
|
<span>8</span>
|
|
|
|
|
|
<span>6</span>
|
|
|
|
|
|
<span>4</span>
|
|
|
|
|
|
<span>2</span>
|
|
|
|
|
|
<span>0</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 柱状图容器 柱子底部对齐0网格线 -->
|
|
|
|
|
|
<div class="ml-6 h-full flex items-end justify-between gap-1 z-10 relative">
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-for="item in chartData"
|
|
|
|
|
|
:key="item.time"
|
|
|
|
|
|
class="flex items-end gap-1 h-full w-full justify-center"
|
|
|
|
|
|
>
|
|
|
|
|
|
<!-- 所有柱子同时动画 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="w-3 bg-primary-yellow rounded-t-sm chart-bar"
|
|
|
|
|
|
:style="{ height: (item.mcp / 8 * 100) + '%' }"
|
|
|
|
|
|
></div>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="w-3 bg-primary-cyan rounded-t-sm chart-bar"
|
|
|
|
|
|
:style="{ height: (item.models / 8 * 100) + '%' }"
|
|
|
|
|
|
></div>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="w-3 bg-primary-purple rounded-t-sm chart-bar"
|
|
|
|
|
|
:style="{ height: (item.agents / 8 * 100) + '%' }"
|
|
|
|
|
|
></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 时间标签区 -->
|
|
|
|
|
|
<div class="ml-6 w-full flex justify-between gap-1 mt-1">
|
|
|
|
|
|
<span v-for="item in chartData" :key="item.time" class="text-xs text-gray-500 w-full text-center">{{ item.time }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 图例 -->
|
|
|
|
|
|
<div class="flex justify-center gap-6 mt-4">
|
|
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
|
<span class="w-3 h-3 rounded-sm bg-primary-purple"></span>
|
|
|
|
|
|
<span class="text-xs text-gray-400">Agents calls</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
|
<span class="w-3 h-3 rounded-sm bg-primary-yellow"></span>
|
|
|
|
|
|
<span class="text-xs text-gray-400">MCP servers calls</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
|
<span class="w-3 h-3 rounded-sm bg-primary-cyan"></span>
|
|
|
|
|
|
<span class="text-xs text-gray-400">Models requests</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Top 10 requests 卡片 -->
|
|
|
|
|
|
<div class="bg-dark-700 rounded-xl p-5">
|
|
|
|
|
|
<h3 class="font-semibold text-lg mb-4">Top 10 requests</h3>
|
|
|
|
|
|
<!-- 标签切换 -->
|
|
|
|
|
|
<div class="flex mb-4 border border-dark-500 rounded-lg overflow-hidden">
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="flex-1 py-2 text-sm font-medium transition-colors"
|
|
|
|
|
|
:class="topRequestsTab === 'general' ? 'bg-dark-500 text-white' : 'bg-dark-600 text-gray-400 hover:text-white'"
|
|
|
|
|
|
@click="topRequestsTab = 'general'"
|
|
|
|
|
|
>
|
|
|
|
|
|
General
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="flex-1 py-2 text-sm font-medium transition-colors"
|
|
|
|
|
|
:class="topRequestsTab === 'errors' ? 'bg-dark-500 text-white' : 'bg-dark-600 text-gray-400 hover:text-white'"
|
|
|
|
|
|
@click="topRequestsTab = 'errors'"
|
|
|
|
|
|
>
|
|
|
|
|
|
Errors
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- 列表 -->
|
|
|
|
|
|
<ul class="space-y-3">
|
|
|
|
|
|
<li v-for="req in topRequests" :key="req.name" class="flex justify-between items-center">
|
|
|
|
|
|
<div class="flex items-center gap-2">
|
|
|
|
|
|
<i :class="['fa-solid', req.type === 'cube' ? 'fa-cube' : 'fa-code', 'text-gray-400']"></i>
|
|
|
|
|
|
<span class="text-sm text-gray-300">{{ req.name }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="text-sm">{{ req.count }}</span>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 第三行 -->
|
|
|
|
|
|
<!-- What's new 卡片(跨2列) -->
|
|
|
|
|
|
<div class="bg-dark-700 rounded-xl p-5 col-span-2">
|
|
|
|
|
|
<div class="flex justify-between items-center mb-4">
|
|
|
|
|
|
<h3 class="font-semibold text-lg">What's new</h3>
|
|
|
|
|
|
<a href="#" class="text-primary-orange text-sm flex items-center gap-1 hover:text-orange-400 transition-colors">
|
|
|
|
|
|
Full change log
|
|
|
|
|
|
<i class="fa-solid fa-arrow-up-right-from-square"></i>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="text-sm text-gray-400 mb-4">Stay up to date with our latest feature and improvements</p>
|
|
|
|
|
|
<!-- 更新列表 -->
|
|
|
|
|
|
<ul class="space-y-4">
|
|
|
|
|
|
<li v-for="item in whatsNew" :key="item.title" class="flex justify-between items-start">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h4 class="font-medium mb-1">{{ item.title }}</h4>
|
|
|
|
|
|
<p class="text-sm text-gray-400">{{ item.desc }}</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<span class="text-xs text-gray-500">{{ item.date }}</span>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Recent requests (10) 卡片 -->
|
|
|
|
|
|
<div class="bg-dark-700 rounded-xl p-5">
|
|
|
|
|
|
<h3 class="font-semibold text-lg mb-4">Recent requests (10)</h3>
|
|
|
|
|
|
<!-- 列表 -->
|
|
|
|
|
|
<ul class="space-y-3">
|
|
|
|
|
|
<li v-for="(req, index) in recentRequests" :key="index" class="flex items-center gap-3">
|
|
|
|
|
|
<i :class="['fa-solid', req.type === 'cube' ? 'fa-cube' : req.type === 'robot' ? 'fa-robot' : 'fa-code', 'text-gray-400']"></i>
|
|
|
|
|
|
<div class="flex-1">
|
|
|
|
|
|
<div class="flex justify-between items-center">
|
|
|
|
|
|
<span class="text-sm font-medium">{{ req.name }}</span>
|
|
|
|
|
|
<span class="text-xs text-gray-500">in {{ req.time }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-center gap-1 text-xs text-primary-success">
|
|
|
|
|
|
<i class="fa-solid fa-circle-check"></i>
|
|
|
|
|
|
<span>{{ req.status }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
2026-03-07 13:53:53 +08:00
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.error-switch {
|
|
|
|
|
|
--el-switch-on-color: #f97316;
|
|
|
|
|
|
--el-switch-off-color: #374151;
|
|
|
|
|
|
}
|
|
|
|
|
|
.error-switch :deep(.el-switch__action) {
|
|
|
|
|
|
background-color: white;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|