fufeigemini / page /src /stores /dashboard.js
Leeflour's picture
Upload 197 files
d0dd276 verified
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'
import { useBackendStore } from './backend'
export const useDashboardStore = defineStore('dashboard', () => {
// 状态
const status = ref({
keyCount: 0,
modelCount: 0,
retryCount: 0,
last24hCalls: 0,
hourlyCalls: 0,
minuteCalls: 0
})
// 添加图表相关的时间序列数据
const timeSeriesData = ref({
calls: [], // API调用时间序列
tokens: [] // Token使用时间序列
})
const config = ref({
maxRequestsPerMinute: 0,
maxRequestsPerDayPerIp: 0,
currentTime: '',
fakeStreaming: false,
fakeStreamingInterval: 0,
randomString: false,
localVersion: '',
remoteVersion: '',
hasUpdate: false,
concurrentRequests: 0,
increaseConcurrentOnFailure: 0,
maxConcurrentRequests: 0,
maxRetryNum: 0,
searchPrompt: '',
maxEmptyResponses: 0
})
const apiKeyStats = ref([])
const logs = ref([])
const isRefreshing = ref(false)
const isConfigLoaded = ref(false)
// 添加模型相关状态
const selectedModel = ref('all')
const availableModels = ref([])
// 夜间模式状态
const isDarkMode = ref(localStorage.getItem('darkMode') === 'true')
// 监听夜间模式变化,保存到localStorage
watch(isDarkMode, (newValue) => {
localStorage.setItem('darkMode', newValue)
applyDarkMode(newValue)
})
// 应用夜间模式
function applyDarkMode(isDark) {
if (isDark) {
document.documentElement.classList.add('dark-mode')
} else {
document.documentElement.classList.remove('dark-mode')
}
}
// 初始应用夜间模式
applyDarkMode(isDarkMode.value)
// 获取仪表盘数据
async function fetchDashboardData() {
if (isRefreshing.value) return // 防止重复请求
const backendStore = useBackendStore()
isRefreshing.value = true
try {
const response = await backendStore.apiRequest('api/dashboard-data')
const data = await response.json()
updateDashboardData(data)
} catch (error) {
console.error('获取数据失败:', error)
// 如果是网络错误,可以显示错误状态
throw error
} finally {
isRefreshing.value = false
}
}
// 更新仪表盘数据
function updateDashboardData(data) {
// 更新状态数据
status.value = {
keyCount: data.key_count || 0,
modelCount: data.model_count || 0,
retryCount: data.retry_count || 0,
last24hCalls: data.last_24h_calls || 0,
hourlyCalls: data.hourly_calls || 0,
minuteCalls: data.minute_calls || 0,
enableVertex: data.enable_vertex || false
}
// 更新时间序列数据
if (data.calls_time_series) {
timeSeriesData.value.calls = data.calls_time_series
}
if (data.tokens_time_series) {
timeSeriesData.value.tokens = data.tokens_time_series
}
// 更新配置数据
config.value = {
maxRequestsPerMinute: data.max_requests_per_minute || 0,
maxRequestsPerDayPerIp: data.max_requests_per_day_per_ip || 0,
currentTime: data.current_time || '',
fakeStreaming: data.fake_streaming || false,
fakeStreamingInterval: data.fake_streaming_interval || 0,
randomString: data.random_string || false,
randomStringLength: data.random_string_length || 0,
searchMode: data.search_mode || false,
searchPrompt: data.search_prompt || '',
localVersion: data.local_version || '',
remoteVersion: data.remote_version || '',
hasUpdate: data.has_update || false,
concurrentRequests: data.concurrent_requests || 0,
increaseConcurrentOnFailure: data.increase_concurrent_on_failure || 0,
maxConcurrentRequests: data.max_concurrent_requests || 0,
enableVertex: data.enable_vertex || false,
enableVertexExpress: data.enable_vertex_express || false,
vertexExpressApiKey: data.vertex_express_api_key || false,
googleCredentialsJson: data.google_credentials_json || false,
maxRetryNum: data.max_retry_num || 0,
maxEmptyResponses: data.max_empty_responses || 0
}
// 更新API密钥统计
if (data.api_key_stats) {
apiKeyStats.value = data.api_key_stats.map(stat => ({
...stat,
// 确保model_stats的每个模型都有calls和tokens两个指标
model_stats: Object.entries(stat.model_stats || {}).reduce((acc, [model, data]) => {
acc[model] = {
calls: typeof data === 'object' ? data.calls : data, // 兼容旧格式
tokens: typeof data === 'object' ? data.tokens : 0
}
return acc
}, {})
}))
// 提取所有可用的模型
const models = new Set(['all']) // 始终包含"全部"选项
data.api_key_stats.forEach(stat => {
if (stat.model_stats) {
Object.keys(stat.model_stats).forEach(model => {
models.add(model)
})
}
})
availableModels.value = Array.from(models)
// 如果当前选择的模型不在可用模型列表中,重置为"all"
if (!availableModels.value.includes(selectedModel.value)) {
selectedModel.value = 'all'
}
}
// 更新日志
if (data.logs) {
logs.value = data.logs
}
isConfigLoaded.value = true
}
// 设置选择的模型
function setSelectedModel(model) {
selectedModel.value = model
}
// 切换夜间模式
function toggleDarkMode() {
isDarkMode.value = !isDarkMode.value
}
// 切换Vertex AI配置
async function toggleVertex() {
try {
const newValue = !config.value.enableVertex
await updateConfig('enableVertex', newValue, '123') // 使用默认密码
// 更新本地状态
config.value.enableVertex = newValue
} catch (error) {
console.error('切换Vertex AI失败:', error)
}
}
// 更新配置项
async function updateConfig(key, value, password) {
const backendStore = useBackendStore()
try {
// 将驼峰命名转换为下划线命名
const snakeCaseKey = key.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
const response = await backendStore.apiRequest('api/update-config', {
method: 'POST',
body: JSON.stringify({
key: snakeCaseKey,
value,
password
})
})
const data = await response.json()
return data
} catch (error) {
console.error('更新配置失败:', error)
throw error
}
}
return {
status,
config,
apiKeyStats,
logs,
isRefreshing,
timeSeriesData, // 导出时间序列数据
fetchDashboardData,
selectedModel,
availableModels,
setSelectedModel,
isDarkMode,
toggleDarkMode,
updateConfig,
toggleVertex,
isConfigLoaded
}
})