interpreter-admin/pages/dashboard.vue

290 lines
9.1 KiB
Vue
Raw Permalink Normal View History

2025-06-26 11:24:11 +08:00
<template>
<div class="space-y-6">
<!-- 统计卡片 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z"></path>
</svg>
</div>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">总用户数</dt>
<dd class="text-lg font-medium text-gray-900">{{ stats.totalUsers }}</dd>
</dl>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-green-500 rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>
</div>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">今日订单</dt>
<dd class="text-lg font-medium text-gray-900">{{ stats.todayOrders }}</dd>
</dl>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-yellow-500 rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1"></path>
</svg>
</div>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">今日收入</dt>
<dd class="text-lg font-medium text-gray-900">¥{{ stats.todayRevenue.toLocaleString() }}</dd>
</dl>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<div class="w-8 h-8 bg-purple-500 rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
</div>
<div class="ml-5 w-0 flex-1">
<dl>
<dt class="text-sm font-medium text-gray-500 truncate">活跃译员</dt>
<dd class="text-lg font-medium text-gray-900">{{ stats.activeTranslators }}</dd>
</dl>
</div>
</div>
</div>
</div>
<!-- 最近活动 -->
<div class="bg-white shadow rounded-lg">
<div class="px-6 py-4 border-b border-gray-200">
<h3 class="text-lg font-medium text-gray-900">最近活动</h3>
</div>
<div class="px-6 py-4">
<div class="flow-root">
<ul class="-my-5 divide-y divide-gray-200">
<li v-for="activity in recentActivities" :key="activity.id" class="py-4">
<div class="flex items-center space-x-4">
<div class="flex-shrink-0">
<div :class="activity.iconColor" class="w-8 h-8 rounded-full flex items-center justify-center">
<span class="text-sm font-medium text-white">{{ activity.icon }}</span>
</div>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900 truncate">{{ activity.title }}</p>
<p class="text-sm text-gray-500">{{ activity.description }}</p>
</div>
<div class="flex-shrink-0 text-sm text-gray-500">{{ activity.time }}</div>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script setup>
// 确保使用默认布局包含Sidebar和管理员认证
definePageMeta({
middleware: 'admin-auth', // 使用管理员认证中间件
layout: 'default' // 明确指定使用默认布局
})
// 页面标题
useHead({
title: '仪表板 - 翻译管理系统'
})
// 临时注释掉Supabase导入
// const { getStats, getCalls } = useSupabaseData()
// 当前用户信息
const currentUser = ref({
name: '系统管理员',
role: '管理员'
})
// 统计数据
const stats = ref({
totalUsers: 0,
todayOrders: 0,
todayRevenue: 0,
activeTranslators: 0
})
// 加载状态
const loading = ref(true)
const error = ref(null)
// 最近活动
const recentActivities = ref([])
// 获取用户信息
onMounted(() => {
loadDashboardData()
// 客户端专用操作
if (process.client) {
const adminUser = localStorage.getItem('adminUser')
if (adminUser) {
try {
const user = JSON.parse(adminUser)
console.log('当前用户:', user)
} catch (error) {
console.error('解析用户信息失败:', error)
}
}
}
})
// 加载仪表板数据
const loadDashboardData = async () => {
try {
loading.value = true
error.value = null
console.log('开始加载仪表板数据...')
// 临时注释掉Supabase调用使用模拟数据
// const statsData = await getStats()
// console.log('统计数据加载成功:', statsData)
// 使用模拟数据
const statsData = {
totalUsers: 156,
totalCalls: 23,
totalRevenue: 12580,
activeInterpreters: 8
}
stats.value = {
totalUsers: statsData.totalUsers || 0,
todayOrders: statsData.totalCalls || 0,
todayRevenue: statsData.totalRevenue || 0,
activeTranslators: statsData.activeInterpreters || 0
}
// 临时注释掉Supabase调用使用模拟数据
// const recentCalls = await getCalls()
// console.log('通话记录加载成功:', recentCalls)
// 使用模拟活动数据
recentActivities.value = [
{
id: 1,
title: '新订单创建',
description: '张先生 - 李译员',
time: '2分钟前',
icon: 'O',
iconColor: 'bg-yellow-400'
},
{
id: 2,
title: '翻译完成',
description: '王女士 - 陈译员',
time: '15分钟前',
icon: '✓',
iconColor: 'bg-green-400'
},
{
id: 3,
title: '翻译进行中',
description: '李总 - 刘译员',
time: '30分钟前',
icon: 'T',
iconColor: 'bg-blue-400'
}
]
console.log('仪表板数据加载成功:', { stats: stats.value, activities: recentActivities.value })
} catch (err) {
console.error('加载仪表板数据失败:', err)
error.value = '加载数据失败,请刷新页面重试'
// 显示默认数据
stats.value = {
totalUsers: 0,
todayOrders: 0,
todayRevenue: 0,
activeTranslators: 0
}
recentActivities.value = []
} finally {
loading.value = false
}
}
// 根据状态获取活动标题
const getActivityTitle = (status) => {
const statusMap = {
'pending': '新订单创建',
'in_progress': '翻译进行中',
'completed': '翻译完成',
'cancelled': '订单取消'
}
return statusMap[status] || '订单更新'
}
// 根据状态获取活动图标
const getActivityIcon = (status) => {
const iconMap = {
'pending': 'O',
'in_progress': 'T',
'completed': '✓',
'cancelled': 'X'
}
return iconMap[status] || 'U'
}
// 根据状态获取活动颜色
const getActivityColor = (status) => {
const colorMap = {
'pending': 'bg-yellow-400',
'in_progress': 'bg-blue-400',
'completed': 'bg-green-400',
'cancelled': 'bg-red-400'
}
return colorMap[status] || 'bg-gray-400'
}
// 格式化时间
const formatTime = (timestamp) => {
if (!timestamp) return '未知时间'
const now = new Date()
const time = new Date(timestamp)
const diff = now - time
const minutes = Math.floor(diff / (1000 * 60))
const hours = Math.floor(diff / (1000 * 60 * 60))
const days = Math.floor(diff / (1000 * 60 * 60 * 24))
if (minutes < 1) return '刚刚'
if (minutes < 60) return `${minutes}分钟前`
if (hours < 24) return `${hours}小时前`
return `${days}天前`
}
</script>