2025-12-18 13:50:39 +08:00
< template >
< AppLayout >
< div class = "space-y-6" >
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
< UsageStatsCards :stats = "usageStats" / >
2026-01-04 20:10:15 -08:00
<!-- Charts Section -- >
< div class = "space-y-4" >
< div class = "card p-4" >
< div class = "flex items-center gap-4" >
< span class = "text-sm font-medium text-gray-700 dark:text-gray-300" > { { t ( 'admin.dashboard.granularity' ) } } : < / span >
< div class = "w-28" >
< Select v-model = "granularity" :options="granularityOptions" @change="loadChartData" / >
< / div >
< / div >
< / div >
< div class = "grid grid-cols-1 gap-6 lg:grid-cols-2" >
< ModelDistributionChart :model-stats = "modelStats" :loading = "chartsLoading" / >
< TokenUsageTrend :trend-data = "trendData" :loading = "chartsLoading" / >
< / div >
< / div >
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
< UsageFilters v-model = "filters" v-model:startDate="startDate" v-model:endDate="endDate" :exporting="exporting" @change="applyFilters" @reset="resetFilters" @export="exportToExcel" / >
< UsageTable :data = "usageLogs" :loading = "loading" / >
< Pagination v-if = "pagination.total > 0" :page="pagination.page" :total="pagination.total" :page-size="pagination.page_size" @update:page="handlePageChange" @update:pageSize="handlePageSizeChange" / >
2025-12-18 13:50:39 +08:00
< / div >
< / AppLayout >
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
< UsageExportProgress :show = "exportProgress.show" :progress = "exportProgress.progress" :current = "exportProgress.current" :total = "exportProgress.total" :estimated-time = "exportProgress.estimatedTime" @cancel ="cancelExport" / >
2025-12-18 13:50:39 +08:00
< / template >
< script setup lang = "ts" >
2026-01-04 20:10:15 -08:00
import { ref , reactive , computed , onMounted , onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'
2026-01-06 11:36:38 +08:00
import { saveAs } from 'file-saver'
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
import { useAppStore } from '@/stores/app' ; import { adminAPI } from '@/api/admin' ; import { adminUsageAPI } from '@/api/admin/usage'
2026-01-04 20:10:15 -08:00
import AppLayout from '@/components/layout/AppLayout.vue' ; import Pagination from '@/components/common/Pagination.vue' ; import Select from '@/components/common/Select.vue'
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
import UsageStatsCards from '@/components/admin/usage/UsageStatsCards.vue' ; import UsageFilters from '@/components/admin/usage/UsageFilters.vue'
import UsageTable from '@/components/admin/usage/UsageTable.vue' ; import UsageExportProgress from '@/components/admin/usage/UsageExportProgress.vue'
2026-01-04 20:10:15 -08:00
import ModelDistributionChart from '@/components/charts/ModelDistributionChart.vue' ; import TokenUsageTrend from '@/components/charts/TokenUsageTrend.vue'
import type { UsageLog , TrendDataPoint , ModelStat } from '@/types' ; import type { AdminUsageStatsResponse , AdminUsageQueryParams } from '@/api/admin/usage'
2025-12-18 13:50:39 +08:00
2026-01-04 20:10:15 -08:00
const { t } = useI18n ( )
2025-12-18 13:50:39 +08:00
const appStore = useAppStore ( )
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
const usageStats = ref < AdminUsageStatsResponse | null > ( null ) ; const usageLogs = ref < UsageLog [ ] > ( [ ] ) ; const loading = ref ( false ) ; const exporting = ref ( false )
2026-01-04 20:10:15 -08:00
const trendData = ref < TrendDataPoint [ ] > ( [ ] ) ; const modelStats = ref < ModelStat [ ] > ( [ ] ) ; const chartsLoading = ref ( false ) ; const granularity = ref < 'day' | 'hour' > ( 'day' )
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
let abortController : AbortController | null = null ; let exportAbortController : AbortController | null = null
const exportProgress = reactive ( { show : false , progress : 0 , current : 0 , total : 0 , estimatedTime : '' } )
2026-01-04 20:10:15 -08:00
const granularityOptions = computed ( ( ) => [ { value : 'day' , label : t ( 'admin.dashboard.day' ) } , { value : 'hour' , label : t ( 'admin.dashboard.hour' ) } ] )
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
const formatLD = ( d : Date ) => d . toISOString ( ) . split ( 'T' ) [ 0 ]
const now = new Date ( ) ; const weekAgo = new Date ( Date . now ( ) - 6 * 86400000 )
const startDate = ref ( formatLD ( weekAgo ) ) ; const endDate = ref ( formatLD ( now ) )
const filters = ref < AdminUsageQueryParams > ( { user _id : undefined , model : undefined , group _id : undefined , start _date : startDate . value , end _date : endDate . value } )
const pagination = reactive ( { page : 1 , page _size : 20 , total : 0 } )
const loadLogs = async ( ) => {
abortController ? . abort ( ) ; const c = new AbortController ( ) ; abortController = c ; loading . value = true
2025-12-20 10:06:55 +08:00
try {
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
const res = await adminAPI . usage . list ( { page : pagination . page , page _size : pagination . page _size , ... filters . value } , { signal : c . signal } )
if ( ! c . signal . aborted ) { usageLogs . value = res . items ; pagination . total = res . total }
2026-01-06 12:42:06 +08:00
} catch ( error : any ) { if ( error ? . name !== 'AbortError' ) console . error ( 'Failed to load usage logs:' , error ) } finally { if ( abortController === c ) loading . value = false }
2025-12-29 16:13:09 +08:00
}
2026-01-06 12:42:06 +08:00
const loadStats = async ( ) => { try { const s = await adminAPI . usage . getStats ( filters . value ) ; usageStats . value = s } catch ( error ) { console . error ( 'Failed to load usage stats:' , error ) } }
2026-01-04 20:10:15 -08:00
const loadChartData = async ( ) => {
chartsLoading . value = true
try {
const params = { start _date : filters . value . start _date || startDate . value , end _date : filters . value . end _date || endDate . value , granularity : granularity . value , user _id : filters . value . user _id }
const [ trendRes , modelRes ] = await Promise . all ( [ adminAPI . dashboard . getUsageTrend ( params ) , adminAPI . dashboard . getModelStats ( { start _date : params . start _date , end _date : params . end _date , user _id : params . user _id } ) ] )
trendData . value = trendRes . trend || [ ] ; modelStats . value = modelRes . models || [ ]
2026-01-06 12:50:51 +08:00
} catch ( error ) { console . error ( 'Failed to load chart data:' , error ) } finally { chartsLoading . value = false }
2026-01-04 20:10:15 -08:00
}
const applyFilters = ( ) => { pagination . page = 1 ; loadLogs ( ) ; loadStats ( ) ; loadChartData ( ) }
const resetFilters = ( ) => { startDate . value = formatLD ( weekAgo ) ; endDate . value = formatLD ( now ) ; filters . value = { start _date : startDate . value , end _date : endDate . value } ; granularity . value = 'day' ; applyFilters ( ) }
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
const handlePageChange = ( p : number ) => { pagination . page = p ; loadLogs ( ) }
const handlePageSizeChange = ( s : number ) => { pagination . page _size = s ; pagination . page = 1 ; loadLogs ( ) }
const cancelExport = ( ) => exportAbortController ? . abort ( )
2025-12-29 16:13:09 +08:00
const exportToExcel = async ( ) => {
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
if ( exporting . value ) return ; exporting . value = true ; exportProgress . show = true
const c = new AbortController ( ) ; exportAbortController = c
2025-12-29 16:13:09 +08:00
try {
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
const all : UsageLog [ ] = [ ] ; let p = 1 ; let total = pagination . total
2025-12-29 16:13:09 +08:00
while ( true ) {
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
const res = await adminUsageAPI . list ( { page : p , page _size : 100 , ... filters . value } , { signal : c . signal } )
if ( c . signal . aborted ) break ; if ( p === 1 ) { total = res . total ; exportProgress . total = total }
if ( res . items ? . length ) all . push ( ... res . items )
exportProgress . current = all . length ; exportProgress . progress = total > 0 ? Math . min ( 100 , Math . round ( all . length / total * 100 ) ) : 0
if ( all . length >= total || res . items . length < 100 ) break ; p ++
2025-12-29 16:13:09 +08:00
}
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
if ( ! c . signal . aborted ) {
2026-01-06 11:36:38 +08:00
const XLSX = await import ( 'xlsx' )
2026-01-07 09:35:21 +08:00
const headers = [
t ( 'usage.time' ) , t ( 'admin.usage.user' ) , t ( 'usage.apiKeyFilter' ) ,
t ( 'admin.usage.account' ) , t ( 'usage.model' ) , t ( 'admin.usage.group' ) ,
t ( 'usage.type' ) ,
t ( 'admin.usage.inputTokens' ) , t ( 'admin.usage.outputTokens' ) ,
t ( 'admin.usage.cacheReadTokens' ) , t ( 'admin.usage.cacheCreationTokens' ) ,
t ( 'admin.usage.inputCost' ) , t ( 'admin.usage.outputCost' ) ,
t ( 'admin.usage.cacheReadCost' ) , t ( 'admin.usage.cacheCreationCost' ) ,
t ( 'usage.rate' ) , t ( 'usage.original' ) , t ( 'usage.billed' ) ,
t ( 'usage.billingType' ) , t ( 'usage.firstToken' ) , t ( 'usage.duration' ) ,
t ( 'admin.usage.requestId' )
]
const rows = all . map ( log => [
log . created _at ,
log . user ? . email || '' ,
log . api _key ? . name || '' ,
log . account ? . name || '' ,
log . model ,
log . group ? . name || '' ,
log . stream ? t ( 'usage.stream' ) : t ( 'usage.sync' ) ,
log . input _tokens ,
log . output _tokens ,
log . cache _read _tokens ,
log . cache _creation _tokens ,
log . input _cost ? . toFixed ( 6 ) || '0.000000' ,
log . output _cost ? . toFixed ( 6 ) || '0.000000' ,
log . cache _read _cost ? . toFixed ( 6 ) || '0.000000' ,
log . cache _creation _cost ? . toFixed ( 6 ) || '0.000000' ,
log . rate _multiplier ? . toFixed ( 2 ) || '1.00' ,
log . total _cost ? . toFixed ( 6 ) || '0.000000' ,
log . actual _cost ? . toFixed ( 6 ) || '0.000000' ,
log . billing _type === 1 ? t ( 'usage.subscription' ) : t ( 'usage.balance' ) ,
log . first _token _ms ? ? '' ,
log . duration _ms ,
log . request _id || ''
] )
const ws = XLSX . utils . aoa _to _sheet ( [ headers , ... rows ] )
const wb = XLSX . utils . book _new ( )
XLSX . utils . book _append _sheet ( wb , ws , 'Usage' )
saveAs ( new Blob ( [ XLSX . write ( wb , { bookType : 'xlsx' , type : 'array' } ) ] , { type : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' } ) , ` usage_ ${ filters . value . start _date } _to_ ${ filters . value . end _date } .xlsx ` )
appStore . showSuccess ( t ( 'usage.exportSuccess' ) )
2025-12-29 16:13:09 +08:00
}
2026-01-06 12:50:51 +08:00
} catch ( error ) { console . error ( 'Failed to export:' , error ) ; appStore . showError ( 'Export Failed' ) }
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
finally { if ( exportAbortController === c ) { exportAbortController = null ; exporting . value = false ; exportProgress . show = false } }
2025-12-27 16:05:16 +08:00
}
2026-01-04 20:10:15 -08:00
onMounted ( ( ) => { loadLogs ( ) ; loadStats ( ) ; loadChartData ( ) } )
refactor(frontend): comprehensive split of large view files into modular components
- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc.
- Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc.
- Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc.
- Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc.
- Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors.
- Improved overall frontend maintainability and code clarity.
2026-01-04 22:17:27 +08:00
onUnmounted ( ( ) => { abortController ? . abort ( ) ; exportAbortController ? . abort ( ) } )
2026-01-06 11:36:38 +08:00
< / script >