时间范围联动数据:Dashboard 管理 rangeKey 状态,useDashboardData 按天数生成 dailyStats

This commit is contained in:
wangxiaoji 2026-06-30 14:35:03 +08:00
parent d2913ff260
commit 65c28251f9
3 changed files with 26 additions and 11 deletions

View File

@ -48,9 +48,10 @@ const BentoTrend = ({
className = '', className = '',
onRefresh, onRefresh,
loading = false, loading = false,
rangeKey = '7d',
onRangeChange,
}) => { }) => {
const [rangeOpen, setRangeOpen] = useState(false); const [rangeOpen, setRangeOpen] = useState(false);
const [selectedKey, setSelectedKey] = useState('7d');
const [customStart, setCustomStart] = useState(''); const [customStart, setCustomStart] = useState('');
const [customEnd, setCustomEnd] = useState(''); const [customEnd, setCustomEnd] = useState('');
const [tooltip, setTooltip] = useState(null); // { dayIdx, x, y } or null const [tooltip, setTooltip] = useState(null); // { dayIdx, x, y } or null
@ -67,11 +68,11 @@ const BentoTrend = ({
return () => document.removeEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler);
}, []); }, []);
const selectedLabel = RANGE_OPTIONS.find((r) => r.key === selectedKey)?.label || '近 7 天'; const selectedLabel = RANGE_OPTIONS.find((r) => r.key === rangeKey)?.label || '近 7 天';
const handleApply = () => { const handleApply = () => {
if (customStart && customEnd) { if (customStart && customEnd) {
setSelectedKey('custom'); onRangeChange('custom');
} }
setRangeOpen(false); setRangeOpen(false);
}; };
@ -150,13 +151,13 @@ const BentoTrend = ({
<button <button
key={opt.key} key={opt.key}
onClick={() => { onClick={() => {
setSelectedKey(opt.key); onRangeChange(opt.key);
setCustomStart(''); setCustomStart('');
setCustomEnd(''); setCustomEnd('');
setRangeOpen(false); setRangeOpen(false);
}} }}
className={`w-full rounded-lg px-3 py-1.5 text-left text-xs transition-colors ${ className={`w-full rounded-lg px-3 py-1.5 text-left text-xs transition-colors ${
selectedKey === opt.key rangeKey === opt.key
? 'bg-blue-50 font-medium text-blue-700 dark:bg-blue-500/10 dark:text-blue-400' ? 'bg-blue-50 font-medium text-blue-700 dark:bg-blue-500/10 dark:text-blue-400'
: 'text-slate-600 hover:bg-slate-50 dark:text-white/60 dark:hover:bg-white/5' : 'text-slate-600 hover:bg-slate-50 dark:text-white/60 dark:hover:bg-white/5'
}`} }`}

View File

@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com For commercial licensing, please contact support@quantumnous.com
*/ */
import React, { useContext } from 'react'; import React, { useContext, useState } from 'react';
import { UserContext } from '../../context/User'; import { UserContext } from '../../context/User';
import { useUserMessageUnreadCount } from '../../hooks/common/useUserMessageUnreadCount'; import { useUserMessageUnreadCount } from '../../hooks/common/useUserMessageUnreadCount';
import { useDashboardData } from '../../hooks/dashboard/useDashboardData'; import { useDashboardData } from '../../hooks/dashboard/useDashboardData';
@ -30,11 +30,23 @@ import BentoTrend from './BentoTrend';
import MonthlyForecast from './MonthlyForecast'; import MonthlyForecast from './MonthlyForecast';
import RecentActivity from './RecentActivity'; import RecentActivity from './RecentActivity';
const RANGE_DAYS = {
today: 1,
yesterday: 1,
'24h': 1,
'7d': 7,
'14d': 14,
'30d': 30,
};
const Dashboard = () => { const Dashboard = () => {
const [userState] = useContext(UserContext); const [userState] = useContext(UserContext);
const user = userState?.user; const user = userState?.user;
const [rangeKey, setRangeKey] = useState('7d');
const dashboard = useDashboardData(userState); const rangeDays = RANGE_DAYS[rangeKey] || 7;
const dashboard = useDashboardData(userState, rangeDays);
const { unreadCount } = useUserMessageUnreadCount(user); const { unreadCount } = useUserMessageUnreadCount(user);
return ( return (
@ -66,6 +78,8 @@ const Dashboard = () => {
topModels={dashboard.topModels} topModels={dashboard.topModels}
onRefresh={dashboard.refresh} onRefresh={dashboard.refresh}
loading={dashboard.loading} loading={dashboard.loading}
rangeKey={rangeKey}
onRangeChange={setRangeKey}
className='md:col-span-2' className='md:col-span-2'
/> />
<MonthlyForecast <MonthlyForecast

View File

@ -28,7 +28,7 @@ const isSuccess = (l) =>
const sum = (arr, key) => const sum = (arr, key) =>
arr.reduce((s, l) => s + (Number(l[key]) || 0), 0); arr.reduce((s, l) => s + (Number(l[key]) || 0), 0);
export const useDashboardData = (userState) => { export const useDashboardData = (userState, rangeDays = 7) => {
const initialized = useRef(false); const initialized = useRef(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [recentLogs, setRecentLogs] = useState([]); const [recentLogs, setRecentLogs] = useState([]);
@ -138,7 +138,7 @@ export const useDashboardData = (userState) => {
const dailyStats = useMemo(() => { const dailyStats = useMemo(() => {
const now = new Date(); const now = new Date();
const days = []; const days = [];
const n = 7; const n = rangeDays;
for (let i = n - 1; i >= 0; i--) { for (let i = n - 1; i >= 0; i--) {
const d = new Date(now.getFullYear(), now.getMonth(), now.getDate() - i); const d = new Date(now.getFullYear(), now.getMonth(), now.getDate() - i);
const dayStart = Math.floor(d.getTime() / 1000); const dayStart = Math.floor(d.getTime() / 1000);
@ -160,7 +160,7 @@ export const useDashboardData = (userState) => {
}); });
} }
return days; return days;
}, [recentLogs]); }, [recentLogs, rangeDays]);
// ========== MoM delta (this month cost vs last month) ========== // ========== MoM delta (this month cost vs last month) ==========
const monthDelta = useMemo(() => { const monthDelta = useMemo(() => {