|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { useState, useEffect, useRef, useCallback, useMemo } from 'react'; |
|
|
import { useNavigate } from 'react-router-dom'; |
|
|
import { useTranslation } from 'react-i18next'; |
|
|
import { API, isAdmin, showError, timestamp2string } from '../../helpers'; |
|
|
import { getDefaultTime, getInitialTimestamp } from '../../helpers/dashboard'; |
|
|
import { TIME_OPTIONS } from '../../constants/dashboard.constants'; |
|
|
import { useIsMobile } from '../common/useIsMobile'; |
|
|
import { useMinimumLoadingTime } from '../common/useMinimumLoadingTime'; |
|
|
|
|
|
export const useDashboardData = (userState, userDispatch, statusState) => { |
|
|
const { t } = useTranslation(); |
|
|
const navigate = useNavigate(); |
|
|
const isMobile = useIsMobile(); |
|
|
const initialized = useRef(false); |
|
|
|
|
|
|
|
|
const [loading, setLoading] = useState(false); |
|
|
const [greetingVisible, setGreetingVisible] = useState(false); |
|
|
const [searchModalVisible, setSearchModalVisible] = useState(false); |
|
|
const showLoading = useMinimumLoadingTime(loading); |
|
|
|
|
|
|
|
|
const [inputs, setInputs] = useState({ |
|
|
username: '', |
|
|
token_name: '', |
|
|
model_name: '', |
|
|
start_timestamp: getInitialTimestamp(), |
|
|
end_timestamp: timestamp2string(new Date().getTime() / 1000 + 3600), |
|
|
channel: '', |
|
|
data_export_default_time: '', |
|
|
}); |
|
|
|
|
|
const [dataExportDefaultTime, setDataExportDefaultTime] = |
|
|
useState(getDefaultTime()); |
|
|
|
|
|
|
|
|
const [quotaData, setQuotaData] = useState([]); |
|
|
const [consumeQuota, setConsumeQuota] = useState(0); |
|
|
const [consumeTokens, setConsumeTokens] = useState(0); |
|
|
const [times, setTimes] = useState(0); |
|
|
const [pieData, setPieData] = useState([{ type: 'null', value: '0' }]); |
|
|
const [lineData, setLineData] = useState([]); |
|
|
const [modelColors, setModelColors] = useState({}); |
|
|
|
|
|
|
|
|
const [activeChartTab, setActiveChartTab] = useState('1'); |
|
|
|
|
|
|
|
|
const [trendData, setTrendData] = useState({ |
|
|
balance: [], |
|
|
usedQuota: [], |
|
|
requestCount: [], |
|
|
times: [], |
|
|
consumeQuota: [], |
|
|
tokens: [], |
|
|
rpm: [], |
|
|
tpm: [], |
|
|
}); |
|
|
|
|
|
|
|
|
const [uptimeData, setUptimeData] = useState([]); |
|
|
const [uptimeLoading, setUptimeLoading] = useState(false); |
|
|
const [activeUptimeTab, setActiveUptimeTab] = useState(''); |
|
|
|
|
|
|
|
|
const now = new Date(); |
|
|
const isAdminUser = isAdmin(); |
|
|
|
|
|
|
|
|
const apiInfoEnabled = statusState?.status?.api_info_enabled ?? true; |
|
|
const announcementsEnabled = |
|
|
statusState?.status?.announcements_enabled ?? true; |
|
|
const faqEnabled = statusState?.status?.faq_enabled ?? true; |
|
|
const uptimeEnabled = statusState?.status?.uptime_kuma_enabled ?? true; |
|
|
|
|
|
const hasApiInfoPanel = apiInfoEnabled; |
|
|
const hasInfoPanels = announcementsEnabled || faqEnabled || uptimeEnabled; |
|
|
|
|
|
|
|
|
const timeOptions = useMemo( |
|
|
() => |
|
|
TIME_OPTIONS.map((option) => ({ |
|
|
...option, |
|
|
label: t(option.label), |
|
|
})), |
|
|
[t], |
|
|
); |
|
|
|
|
|
const performanceMetrics = useMemo(() => { |
|
|
const { start_timestamp, end_timestamp } = inputs; |
|
|
const timeDiff = |
|
|
(Date.parse(end_timestamp) - Date.parse(start_timestamp)) / 60000; |
|
|
const avgRPM = isNaN(times / timeDiff) |
|
|
? '0' |
|
|
: (times / timeDiff).toFixed(3); |
|
|
const avgTPM = isNaN(consumeTokens / timeDiff) |
|
|
? '0' |
|
|
: (consumeTokens / timeDiff).toFixed(3); |
|
|
|
|
|
return { avgRPM, avgTPM, timeDiff }; |
|
|
}, [times, consumeTokens, inputs.start_timestamp, inputs.end_timestamp]); |
|
|
|
|
|
const getGreeting = useMemo(() => { |
|
|
const hours = new Date().getHours(); |
|
|
let greeting = ''; |
|
|
|
|
|
if (hours >= 5 && hours < 12) { |
|
|
greeting = t('早上好'); |
|
|
} else if (hours >= 12 && hours < 14) { |
|
|
greeting = t('中午好'); |
|
|
} else if (hours >= 14 && hours < 18) { |
|
|
greeting = t('下午好'); |
|
|
} else { |
|
|
greeting = t('晚上好'); |
|
|
} |
|
|
|
|
|
const username = userState?.user?.username || ''; |
|
|
return `👋${greeting},${username}`; |
|
|
}, [t, userState?.user?.username]); |
|
|
|
|
|
|
|
|
const handleInputChange = useCallback((value, name) => { |
|
|
if (name === 'data_export_default_time') { |
|
|
setDataExportDefaultTime(value); |
|
|
localStorage.setItem('data_export_default_time', value); |
|
|
return; |
|
|
} |
|
|
setInputs((inputs) => ({ ...inputs, [name]: value })); |
|
|
}, []); |
|
|
|
|
|
const showSearchModal = useCallback(() => { |
|
|
setSearchModalVisible(true); |
|
|
}, []); |
|
|
|
|
|
const handleCloseModal = useCallback(() => { |
|
|
setSearchModalVisible(false); |
|
|
}, []); |
|
|
|
|
|
|
|
|
const loadQuotaData = useCallback(async () => { |
|
|
setLoading(true); |
|
|
try { |
|
|
let url = ''; |
|
|
const { start_timestamp, end_timestamp, username } = inputs; |
|
|
let localStartTimestamp = Date.parse(start_timestamp) / 1000; |
|
|
let localEndTimestamp = Date.parse(end_timestamp) / 1000; |
|
|
|
|
|
if (isAdminUser) { |
|
|
url = `/api/data/?username=${username}&start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&default_time=${dataExportDefaultTime}`; |
|
|
} else { |
|
|
url = `/api/data/self/?start_timestamp=${localStartTimestamp}&end_timestamp=${localEndTimestamp}&default_time=${dataExportDefaultTime}`; |
|
|
} |
|
|
|
|
|
const res = await API.get(url); |
|
|
const { success, message, data } = res.data; |
|
|
if (success) { |
|
|
setQuotaData(data); |
|
|
if (data.length === 0) { |
|
|
data.push({ |
|
|
count: 0, |
|
|
model_name: '无数据', |
|
|
quota: 0, |
|
|
created_at: now.getTime() / 1000, |
|
|
}); |
|
|
} |
|
|
data.sort((a, b) => a.created_at - b.created_at); |
|
|
return data; |
|
|
} else { |
|
|
showError(message); |
|
|
return []; |
|
|
} |
|
|
} finally { |
|
|
setLoading(false); |
|
|
} |
|
|
}, [inputs, dataExportDefaultTime, isAdminUser, now]); |
|
|
|
|
|
const loadUptimeData = useCallback(async () => { |
|
|
setUptimeLoading(true); |
|
|
try { |
|
|
const res = await API.get('/api/uptime/status'); |
|
|
const { success, message, data } = res.data; |
|
|
if (success) { |
|
|
setUptimeData(data || []); |
|
|
if (data && data.length > 0 && !activeUptimeTab) { |
|
|
setActiveUptimeTab(data[0].categoryName); |
|
|
} |
|
|
} else { |
|
|
showError(message); |
|
|
} |
|
|
} catch (err) { |
|
|
console.error(err); |
|
|
} finally { |
|
|
setUptimeLoading(false); |
|
|
} |
|
|
}, [activeUptimeTab]); |
|
|
|
|
|
const getUserData = useCallback(async () => { |
|
|
let res = await API.get(`/api/user/self`); |
|
|
const { success, message, data } = res.data; |
|
|
if (success) { |
|
|
userDispatch({ type: 'login', payload: data }); |
|
|
} else { |
|
|
showError(message); |
|
|
} |
|
|
}, [userDispatch]); |
|
|
|
|
|
const refresh = useCallback(async () => { |
|
|
const data = await loadQuotaData(); |
|
|
await loadUptimeData(); |
|
|
return data; |
|
|
}, [loadQuotaData, loadUptimeData]); |
|
|
|
|
|
const handleSearchConfirm = useCallback( |
|
|
async (updateChartDataCallback) => { |
|
|
const data = await refresh(); |
|
|
if (data && data.length > 0 && updateChartDataCallback) { |
|
|
updateChartDataCallback(data); |
|
|
} |
|
|
setSearchModalVisible(false); |
|
|
}, |
|
|
[refresh], |
|
|
); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
const timer = setTimeout(() => { |
|
|
setGreetingVisible(true); |
|
|
}, 100); |
|
|
return () => clearTimeout(timer); |
|
|
}, []); |
|
|
|
|
|
useEffect(() => { |
|
|
if (!initialized.current) { |
|
|
getUserData(); |
|
|
initialized.current = true; |
|
|
} |
|
|
}, [getUserData]); |
|
|
|
|
|
return { |
|
|
|
|
|
loading: showLoading, |
|
|
greetingVisible, |
|
|
searchModalVisible, |
|
|
|
|
|
|
|
|
inputs, |
|
|
dataExportDefaultTime, |
|
|
|
|
|
|
|
|
quotaData, |
|
|
consumeQuota, |
|
|
setConsumeQuota, |
|
|
consumeTokens, |
|
|
setConsumeTokens, |
|
|
times, |
|
|
setTimes, |
|
|
pieData, |
|
|
setPieData, |
|
|
lineData, |
|
|
setLineData, |
|
|
modelColors, |
|
|
setModelColors, |
|
|
|
|
|
|
|
|
activeChartTab, |
|
|
setActiveChartTab, |
|
|
|
|
|
|
|
|
trendData, |
|
|
setTrendData, |
|
|
|
|
|
|
|
|
uptimeData, |
|
|
uptimeLoading, |
|
|
activeUptimeTab, |
|
|
setActiveUptimeTab, |
|
|
|
|
|
|
|
|
timeOptions, |
|
|
performanceMetrics, |
|
|
getGreeting, |
|
|
isAdminUser, |
|
|
hasApiInfoPanel, |
|
|
hasInfoPanels, |
|
|
apiInfoEnabled, |
|
|
announcementsEnabled, |
|
|
faqEnabled, |
|
|
uptimeEnabled, |
|
|
|
|
|
|
|
|
handleInputChange, |
|
|
showSearchModal, |
|
|
handleCloseModal, |
|
|
loadQuotaData, |
|
|
loadUptimeData, |
|
|
getUserData, |
|
|
refresh, |
|
|
handleSearchConfirm, |
|
|
|
|
|
|
|
|
navigate, |
|
|
t, |
|
|
isMobile, |
|
|
}; |
|
|
}; |
|
|
|