feat(ui): 添加克制页面动效

This commit is contained in:
2026-05-12 00:30:16 +08:00
parent 602f969117
commit 2dabbd1281
3 changed files with 164 additions and 36 deletions
+13 -12
View File
@@ -5,6 +5,7 @@ import { ArrowLeft, Battery, BatteryCharging, BatteryLow, FilterX, Search, Zap }
import { useEffect, useMemo, useState } from 'react'
import { z } from 'zod'
import { orpc } from '@/client/orpc'
import { MotionCardDiv, MotionHeader, MotionSection, MotionTableRow } from '@/components/motion'
import { Badge, Button, Card, Input, SectionTitle, Select } from '@/components/ui'
import type { BatteryInfo, BatteryListSort, PowerStatus } from '@/domain/battery'
import { BATTERY_LIST_SORT, BATTERY_LIST_SORT_VALUES, POWER_STATUS, POWER_STATUS_VALUES } from '@/domain/battery'
@@ -240,7 +241,7 @@ function BatteriesPage() {
</div>
<div className="relative z-10 mx-auto max-w-7xl px-6 py-8">
<header>
<MotionHeader>
<div className="flex flex-col gap-5 lg:flex-row lg:items-end lg:justify-between">
<div>
<Badge variant="info" className="mb-4">
@@ -265,28 +266,28 @@ function BatteriesPage() {
</div>
<dl className="mt-8 grid grid-cols-1 gap-4 sm:grid-cols-3">
<Card className="p-5">
<MotionCardDiv className="rounded-2xl border border-white/[0.08] bg-zinc-950/60 shadow-2xl shadow-black/20 p-5">
<dt className="flex items-center gap-2 text-xs font-medium text-zinc-500">
<Battery className="size-4 text-zinc-400" />
</dt>
<dd className="mt-3 text-3xl font-light tabular-nums text-white">{data?.total ?? '-'}</dd>
</Card>
<Card className="p-5">
</MotionCardDiv>
<MotionCardDiv className="rounded-2xl border border-white/[0.08] bg-zinc-950/60 shadow-2xl shadow-black/20 p-5">
<dt className="flex items-center gap-2 text-xs font-medium text-zinc-500">
<BatteryLow className="size-4 text-red-400" />
</dt>
<dd className="mt-3 text-3xl font-light tabular-nums text-red-300">{data?.lowPower ?? '-'}</dd>
</Card>
<Card className="p-5">
</MotionCardDiv>
<MotionCardDiv className="rounded-2xl border border-white/[0.08] bg-zinc-950/60 shadow-2xl shadow-black/20 p-5">
<dt className="flex items-center gap-2 text-xs font-medium text-zinc-500">
<BatteryCharging className="size-4 text-teal-300" />
</dt>
<dd className="mt-3 text-3xl font-light tabular-nums text-teal-300">{data?.charging ?? '-'}</dd>
</Card>
</MotionCardDiv>
</dl>
</header>
</MotionHeader>
<section className="mt-10">
<MotionSection delay={0.1} className="mt-10">
<Card className="mb-6 p-5">
<div className="mb-5 flex flex-wrap items-center justify-between gap-3">
<SectionTitle
@@ -447,13 +448,13 @@ function BatteriesPage() {
</tr>
) : (
table.getRowModel().rows.map((row) => (
<tr key={row.id} className="transition-colors hover:bg-white/[0.02]">
<MotionTableRow key={row.id} className="transition-colors hover:bg-white/[0.02]">
{row.getVisibleCells().map((cell) => (
<td key={cell.id} className="px-6 py-4 whitespace-nowrap">
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
</MotionTableRow>
))
)}
</tbody>
@@ -479,7 +480,7 @@ function BatteriesPage() {
</Button>
</div>
</div>
</section>
</MotionSection>
</div>
</main>
)
+25 -24
View File
@@ -14,6 +14,7 @@ import {
} from 'recharts'
import type { NameType, ValueType } from 'recharts/types/component/DefaultTooltipContent'
import { orpc } from '@/client/orpc'
import { MotionCardArticle, MotionCardDiv, MotionHeader, MotionSection } from '@/components/motion'
import { Badge, Card, SectionTitle } from '@/components/ui'
import type { DashboardSnapshot, DeviceStatus } from '@/domain/battery'
import { BATTERY_LIST_SORT, DEVICE_STATUS } from '@/domain/battery'
@@ -147,7 +148,7 @@ function Dashboard() {
<div className="relative z-10 mx-auto max-w-[1400px] px-6 pb-24 pt-12 lg:px-12">
{/* Header */}
<header className="animate-fade-up mb-12 flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
<MotionHeader className="mb-12 flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
<div className="max-w-3xl">
<Badge variant="info" className="mb-4">
<Activity className="size-3.5" />
@@ -165,18 +166,18 @@ function Dashboard() {
<ArrowRight className="size-3" />
</Link>
</div>
</header>
</MotionHeader>
{/* Executive Summary */}
<section className="animate-fade-up delay-100 mb-12 rounded-xl border border-teal-900/30 bg-teal-950/10 p-6">
<MotionSection delay={0.1} className="mb-12 rounded-xl border border-teal-900/30 bg-teal-950/10 p-6">
<h2 className="mb-3 text-sm font-medium text-teal-400"></h2>
<p className="text-base leading-relaxed text-[#A1A1AA]">{executiveSummary}</p>
</section>
</MotionSection>
{/* Primary KPI Row */}
<section className="animate-fade-up delay-200 mb-12 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-5">
<MotionSection delay={0.2} className="mb-12 grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-5">
{/* Hero KPI */}
<article className="relative overflow-hidden rounded-2xl border border-white/10 bg-white/[0.03] p-8 lg:col-span-2">
<MotionCardArticle className="relative overflow-hidden rounded-2xl border border-white/10 bg-white/[0.03] p-8 lg:col-span-2">
<div className="absolute inset-x-0 top-0 h-1 bg-gradient-to-r from-teal-500/50 to-transparent" />
<p className="text-sm font-medium text-[#A1A1AA]"></p>
<div className="mt-4 flex items-baseline gap-2">
@@ -191,10 +192,10 @@ function Dashboard() {
<span className="text-[#71717A]">|</span>
<span className="text-[#A1A1AA]"> {totalDevices} </span>
</div>
</article>
</MotionCardArticle>
{/* Regular KPIs */}
<article className="rounded-2xl border border-white/[0.06] bg-white/[0.02] p-6 transition-colors hover:border-white/10">
<MotionCardArticle className="rounded-2xl border border-white/[0.06] bg-white/[0.02] p-6 transition-colors hover:border-white/10">
<p className="text-sm text-[#A1A1AA]">30 </p>
<div className="mt-3 flex items-baseline gap-1">
<h2 className="text-4xl font-light tabular-nums text-white">{formatPercent(avgSoh30d)}</h2>
@@ -204,9 +205,9 @@ function Dashboard() {
<span className="text-red-400"></span>
<span className="tabular-nums text-[#A1A1AA]">{formatDelta(avgSoh, avgSoh30d)}</span>
</div>
</article>
</MotionCardArticle>
<article className="rounded-2xl border border-white/[0.06] bg-white/[0.02] p-6 transition-colors hover:border-white/10">
<MotionCardArticle className="rounded-2xl border border-white/[0.06] bg-white/[0.02] p-6 transition-colors hover:border-white/10">
<p className="text-sm text-[#A1A1AA]">90 </p>
<div className="mt-3 flex items-baseline gap-1">
<h2 className="text-4xl font-light tabular-nums text-white">{formatPercent(avgSoh90d)}</h2>
@@ -216,9 +217,9 @@ function Dashboard() {
<span className="text-red-400"></span>
<span className="tabular-nums text-[#A1A1AA]">{formatDelta(avgSoh, avgSoh90d)}</span>
</div>
</article>
</MotionCardArticle>
<article className="rounded-2xl border border-white/[0.06] bg-white/[0.02] p-6 transition-colors hover:border-white/10">
<MotionCardArticle className="rounded-2xl border border-white/[0.06] bg-white/[0.02] p-6 transition-colors hover:border-white/10">
<p className="text-sm text-[#A1A1AA]"></p>
<div className="mt-3 flex items-baseline gap-1">
<h2 className="text-4xl font-light tabular-nums text-white">{warningCount}</h2>
@@ -230,14 +231,14 @@ function Dashboard() {
{totalDevices > 0 ? ((warningCount / totalDevices) * 100).toFixed(1) : 0}%
</span>
</div>
</article>
</section>
</MotionCardArticle>
</MotionSection>
{/* Divider */}
<hr className="my-12 border-white/5" />
{/* Health trend chart */}
<section className="animate-fade-up delay-300 mb-12">
<MotionSection delay={0.3} className="mb-12">
<Card className="p-8">
<header className="mb-8 flex flex-wrap items-end justify-between gap-4">
<SectionTitle
@@ -359,10 +360,10 @@ function Dashboard() {
)}
</div>
</Card>
</section>
</MotionSection>
{/* Two-column grid */}
<section className="animate-fade-up delay-400 mb-12 grid grid-cols-1 gap-8 lg:grid-cols-2">
<MotionSection delay={0.4} className="mb-12 grid grid-cols-1 gap-8 lg:grid-cols-2">
{/* Left Column */}
<div className="space-y-8">
{/* Risk Distribution */}
@@ -504,13 +505,13 @@ function Dashboard() {
</div>
</div>
</div>
</section>
</MotionSection>
{/* Divider */}
<hr className="my-12 border-white/5" />
{/* Device Table */}
<section className="animate-fade-up delay-500 mb-12">
<MotionSection delay={0.5} className="mb-12">
<div className="mb-6 flex items-end justify-between">
<div>
<h3 className="text-xl font-medium text-white"></h3>
@@ -594,17 +595,17 @@ function Dashboard() {
</table>
</div>
</Card>
</section>
</MotionSection>
{/* Bottom Row */}
<section className="animate-fade-up delay-500 grid grid-cols-1 gap-8 lg:grid-cols-3">
<MotionSection delay={0.5} className="grid grid-cols-1 gap-8 lg:grid-cols-3">
{/* Strategy Cards */}
<div className="lg:col-span-2">
<h3 className="mb-6 text-lg font-medium text-white"></h3>
<div className="grid gap-4 sm:grid-cols-2">
{strategies.length > 0 ? (
strategies.map((item, index) => (
<div
<MotionCardDiv
key={item.name}
className="relative overflow-hidden rounded-xl border border-white/[0.06] bg-white/[0.02] p-5"
>
@@ -617,7 +618,7 @@ function Dashboard() {
<span>: {item.scope}</span>
<span>: {item.eta}</span>
</div>
</div>
</MotionCardDiv>
))
) : (
<div className="text-sm text-[#71717A] col-span-2"></div>
@@ -649,7 +650,7 @@ function Dashboard() {
</div>
</div>
</div>
</section>
</MotionSection>
</div>
</main>
)