175 lines
5.5 KiB
TypeScript
175 lines
5.5 KiB
TypeScript
import { ChevronDown, RefreshCw, Truck } from 'lucide-react';
|
|
import { useEffect, useRef, useState } from 'react';
|
|
import type { HydrogenVehicleScope } from '../../api';
|
|
import { formatRefreshTime } from '../model';
|
|
|
|
interface BiYearSelectProps {
|
|
value: number;
|
|
years: number[];
|
|
onChange: (year: number) => void;
|
|
}
|
|
|
|
function BiYearSelect({ value, years, onChange }: BiYearSelectProps) {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const ref = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
function handleClickOutside(e: MouseEvent) {
|
|
if (ref.current && !ref.current.contains(e.target as Node)) {
|
|
setIsOpen(false);
|
|
}
|
|
}
|
|
if (isOpen) {
|
|
document.addEventListener('mousedown', handleClickOutside);
|
|
}
|
|
return () => {
|
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
};
|
|
}, [isOpen]);
|
|
|
|
return (
|
|
<div className="ehb-year-select-wrapper" ref={ref}>
|
|
<button
|
|
type="button"
|
|
className={`ehb-year-select-btn ${isOpen ? 'is-active' : ''}`}
|
|
onClick={() => setIsOpen(!isOpen)}
|
|
aria-label="年份选择"
|
|
>
|
|
<span className="ehb-year-text">{value} 年</span>
|
|
<ChevronDown
|
|
size={13}
|
|
style={{
|
|
transition: 'transform 0.2s ease',
|
|
transform: isOpen ? 'rotate(180deg)' : 'none',
|
|
color: '#64748b',
|
|
}}
|
|
/>
|
|
</button>
|
|
|
|
{isOpen && (
|
|
<div className="ehb-year-dropdown">
|
|
<div className="ehb-year-dropdown__header">切换数据年份</div>
|
|
<div className="ehb-year-dropdown__list">
|
|
{years.map((y) => (
|
|
<button
|
|
key={y}
|
|
type="button"
|
|
className={`ehb-year-dropdown__item ${y === value ? 'is-selected' : ''}`}
|
|
onClick={() => {
|
|
onChange(y);
|
|
setIsOpen(false);
|
|
}}
|
|
>
|
|
<span>{y} 年</span>
|
|
{y === value && <span className="ehb-year-check">✓</span>}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
interface OverviewHeaderProps {
|
|
activeYear: number;
|
|
availableYears: number[];
|
|
vehicleScope: HydrogenVehicleScope;
|
|
verifyScope?: 'all' | 'verified';
|
|
onVerifyScopeChange?: (scope: 'all' | 'verified') => void;
|
|
selectedStationId?: number | null;
|
|
selectedStationName?: string | null;
|
|
stations?: { id: number; name: string }[];
|
|
latestLedgerTime?: string | null;
|
|
lastRefreshAt?: number;
|
|
refreshing?: boolean;
|
|
onSelectYear: (year: number) => void;
|
|
onVehicleScopeChange: (scope: HydrogenVehicleScope) => void;
|
|
onStationChange?: (stationId: number | null) => void;
|
|
onRefresh: () => void;
|
|
}
|
|
|
|
export function OverviewHeader({
|
|
activeYear,
|
|
availableYears,
|
|
vehicleScope,
|
|
verifyScope = 'all',
|
|
onVerifyScopeChange,
|
|
latestLedgerTime,
|
|
lastRefreshAt = 0,
|
|
refreshing = false,
|
|
onSelectYear,
|
|
onVehicleScopeChange,
|
|
onRefresh,
|
|
}: OverviewHeaderProps) {
|
|
return (
|
|
<section className="ehb-daily-filter-card" style={{ marginBottom: 12 }} aria-label="总览筛选工具栏">
|
|
<div className="ehb-daily-filter-row">
|
|
<div className="ehb-daily-filter-group">
|
|
<BiYearSelect value={activeYear} years={availableYears} onChange={onSelectYear} />
|
|
<div className="ehb-pill-tabs">
|
|
<button
|
|
type="button"
|
|
className={`ehb-pill-btn ${verifyScope === 'all' ? 'is-active' : ''}`}
|
|
onClick={() => onVerifyScopeChange?.('all')}
|
|
>
|
|
全量订单
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`ehb-pill-btn ${verifyScope === 'verified' ? 'is-active' : ''}`}
|
|
onClick={() => onVerifyScopeChange?.('verified')}
|
|
title="真实账本已返回全量有效订单"
|
|
>
|
|
仅已核对
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="ehb-daily-filter-group">
|
|
<div className="ehb-fleet-segmented">
|
|
<button
|
|
type="button"
|
|
className={`ehb-fleet-btn ${vehicleScope === 'all' ? 'is-active' : ''}`}
|
|
onClick={() => onVehicleScopeChange('all')}
|
|
>
|
|
全部车辆
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`ehb-fleet-btn ${vehicleScope === 'lingniu' ? 'is-active' : ''}`}
|
|
onClick={() => onVehicleScopeChange('lingniu')}
|
|
>
|
|
<Truck size={14} />
|
|
仅羚牛车辆
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`ehb-fleet-btn ${vehicleScope === 'external' ? 'is-active' : ''}`}
|
|
onClick={() => onVehicleScopeChange('external')}
|
|
>
|
|
<Truck size={14} />
|
|
仅外部车辆
|
|
</button>
|
|
</div>
|
|
|
|
<span className="ehb-chrome__clock" style={{ fontSize: 12, color: '#64748b' }}>
|
|
{latestLedgerTime ? `账本 ${latestLedgerTime.slice(5, 16)}` : lastRefreshAt ? formatRefreshTime(lastRefreshAt) : '已同步'}
|
|
</span>
|
|
|
|
<button
|
|
type="button"
|
|
className="ehb-btn ehb-btn--ghost"
|
|
onClick={onRefresh}
|
|
disabled={refreshing}
|
|
title="数据刷新"
|
|
>
|
|
<RefreshCw size={14} className={refreshing ? 'animate-spin' : ''} aria-hidden />
|
|
刷新
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|