213 lines
5.0 KiB
Vue
Raw Normal View History

<template>
<view class="home-page">
<!-- Welcome -->
<view class="welcome">
<text class="greeting">{{ user?.name ? `${user.name},你好` : '你好' }}</text>
<text class="desc">欢迎使用安知充充电柜</text>
</view>
<!-- Stats Grid -->
<view class="stats-grid">
<view class="stat-card">
<text class="stat-value blue">{{ data?.online_cabinets ?? 0 }}</text>
<text class="stat-label">在线设备</text>
</view>
<view class="stat-card">
<text class="stat-value green">{{ data?.charging_count ?? 0 }}</text>
<text class="stat-label">充电中</text>
</view>
<view class="stat-card">
<text class="stat-value yellow">{{ data?.idle_channels ?? 0 }}</text>
<text class="stat-label">空闲仓体</text>
</view>
<view class="stat-card">
<text class="stat-value" :class="data?.fault_channels ? 'red' : 'gray'">
{{ data?.fault_channels ?? 0 }}
</text>
<text class="stat-label">故障</text>
</view>
</view>
<!-- Today summary -->
<view class="card">
<text class="card-title">今日概况</text>
<view class="today-row">
<view class="today-item">
<text class="today-value">{{ data?.today_charge_count ?? 0 }}</text>
<text class="today-label">充电次数</text>
</view>
<view class="today-item">
<text class="today-value">{{ data?.today_energy_kwh?.toFixed(1) ?? '0.0' }}</text>
<text class="today-label">充电量(kWh)</text>
</view>
<view class="today-item">
<text class="today-value">{{ data?.today_charge_hours?.toFixed(1) ?? '0.0' }}</text>
<text class="today-label">时长(h)</text>
</view>
</view>
</view>
<!-- Fault alerts -->
<view v-if="data?.alerts && data.alerts.length > 0" class="card">
<text class="card-title red-text">故障告警</text>
<view v-for="(alert, i) in data.alerts.slice(0, 5)" :key="i" class="alert-item">
<text class="alert-text">{{ alert.cabinet }} — 通道 {{ alert.channel }}</text>
<text class="alert-project">{{ alert.project }}</text>
</view>
</view>
<!-- Quick Actions -->
<view class="card">
<text class="card-title">快捷操作</text>
<view class="action-item" @click="goDevices">
<text class="action-text">查看设备</text>
</view>
<view class="action-item" @click="goMe">
<text class="action-text">个人中心</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { h5Api, type DashboardData } from '../../api'
import { useAuthStore } from '../../store/auth'
const authStore = useAuthStore()
const user = authStore.user
const data = ref<DashboardData | null>(null)
onShow(() => {
if (!authStore.isAuthenticated) {
uni.navigateTo({ url: '/pages/login/index' })
return
}
fetchDashboard()
})
async function fetchDashboard() {
try {
const res = await h5Api.getDashboard()
data.value = res.data
} catch {
// 静默失败,页面显示零值
}
}
function goDevices() {
uni.switchTab({ url: '/pages/devices/index' })
}
function goMe() {
uni.switchTab({ url: '/pages/me/index' })
}
</script>
<style scoped>
.home-page {
padding: 24px;
}
.welcome {
margin-bottom: 32px;
}
.greeting {
font-size: 36px;
font-weight: 600;
color: #111;
display: block;
}
.desc {
font-size: 26px;
color: #6b7280;
margin-top: 8px;
}
.stats-grid {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin-bottom: 32px;
}
.stat-card {
width: calc(50% - 8px);
background-color: #fff;
border-radius: 16px;
padding: 24px;
}
.stat-value {
font-size: 40px;
font-weight: 700;
display: block;
}
.stat-value.blue { color: #2563eb; }
.stat-value.green { color: #16a34a; }
.stat-value.yellow { color: #f59e0b; }
.stat-value.red { color: #dc2626; }
.stat-value.gray { color: #6b7280; }
.stat-label {
font-size: 24px;
color: #6b7280;
margin-top: 4px;
}
.card {
background-color: #fff;
border-radius: 16px;
padding: 24px;
margin-bottom: 24px;
}
.card-title {
font-size: 28px;
font-weight: 600;
color: #111;
margin-bottom: 16px;
display: block;
}
.red-text { color: #dc2626; }
.today-row {
display: flex;
justify-content: space-between;
}
.today-item {
flex: 1;
text-align: center;
}
.today-value {
font-size: 32px;
font-weight: 700;
color: #111;
display: block;
}
.today-label {
font-size: 22px;
color: #6b7280;
}
.alert-item {
padding: 12px 0;
border-bottom: 1px solid #f3f4f6;
}
.alert-item:last-child {
border-bottom: none;
}
.alert-text {
font-size: 26px;
color: #111;
display: block;
}
.alert-project {
font-size: 22px;
color: #9ca3af;
}
.action-item {
padding: 20px 0;
border-bottom: 1px solid #f3f4f6;
}
.action-item:last-child {
border-bottom: none;
}
.action-text {
font-size: 28px;
color: #374151;
}
</style>