Unverified Commit a413fa3b authored by 程序猿MT's avatar 程序猿MT Committed by GitHub
Browse files

Merge branch 'Wei-Shaw:main' into main

parents 3a8dbf5a 254f1254
<template>
<AppLayout>
<div class="space-y-6">
<!-- Page Header Actions -->
<TablePageLayout>
<template #actions>
<div class="flex justify-end gap-3">
<button
@click="loadApiKeys"
......@@ -36,9 +36,9 @@
{{ t('keys.createKey') }}
</button>
</div>
</template>
<!-- API Keys Table -->
<div class="card overflow-hidden">
<template #table>
<DataTable :columns="columns" :data="apiKeys" :loading="loading">
<template #cell-key="{ value, row }">
<div class="flex items-center gap-2">
......@@ -146,7 +146,7 @@
</template>
<template #cell-created_at="{ value }">
<span class="text-sm text-gray-500 dark:text-dark-400">{{ formatDate(value) }}</span>
<span class="text-sm text-gray-500 dark:text-dark-400">{{ formatDateTime(value) }}</span>
</template>
<template #cell-actions="{ row }">
......@@ -235,7 +235,7 @@
<button
@click="editKey(row)"
class="rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-primary-600 dark:hover:bg-dark-700 dark:hover:text-primary-400"
title="Edit"
:title="t('common.edit')"
>
<svg
class="h-4 w-4"
......@@ -255,7 +255,7 @@
<button
@click="confirmDelete(row)"
class="rounded-lg p-2 text-gray-500 transition-colors hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-900/20 dark:hover:text-red-400"
title="Delete"
:title="t('common.delete')"
>
<svg
class="h-4 w-4"
......@@ -283,9 +283,9 @@
/>
</template>
</DataTable>
</div>
</template>
<!-- Pagination -->
<template #pagination>
<Pagination
v-if="pagination.total > 0"
:page="pagination.page"
......@@ -293,7 +293,8 @@
:page-size="pagination.page_size"
@update:page="handlePageChange"
/>
</div>
</template>
</TablePageLayout>
<!-- Create/Edit Modal -->
<Modal
......@@ -496,6 +497,7 @@ import { useAppStore } from '@/stores/app'
const { t } = useI18n()
import { keysAPI, authAPI, usageAPI, userGroupsAPI } from '@/api'
import AppLayout from '@/components/layout/AppLayout.vue'
import TablePageLayout from '@/components/layout/TablePageLayout.vue'
import DataTable from '@/components/common/DataTable.vue'
import Pagination from '@/components/common/Pagination.vue'
import Modal from '@/components/common/Modal.vue'
......@@ -507,6 +509,7 @@ import GroupBadge from '@/components/common/GroupBadge.vue'
import type { ApiKey, Group, PublicSettings, SubscriptionType, GroupPlatform } from '@/types'
import type { Column } from '@/components/common/types'
import type { BatchApiKeyUsageStats } from '@/api/usage'
import { formatDateTime } from '@/utils/format'
interface GroupOption {
value: number
......@@ -624,15 +627,6 @@ const copyToClipboard = async (text: string, keyId: number) => {
}
}
const formatDate = (dateString: string): string => {
const date = new Date(dateString)
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric'
})
}
const loadApiKeys = async () => {
loading.value = true
try {
......
......@@ -17,7 +17,7 @@
/>
<StatCard
:title="t('profile.memberSince')"
:value="formatMemberSince(user?.created_at || '')"
:value="formatDate(user?.created_at || '', 'YYYY-MM')"
:icon="CalendarIcon"
icon-variant="primary"
/>
......@@ -267,6 +267,7 @@ import { ref, computed, h, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAuthStore } from '@/stores/auth'
import { useAppStore } from '@/stores/app'
import { formatDate } from '@/utils/format'
const { t } = useI18n()
import { userAPI, authAPI } from '@/api'
......@@ -358,15 +359,6 @@ const formatCurrency = (value: number): string => {
return `$${value.toFixed(2)}`
}
const formatMemberSince = (dateString: string): string => {
if (!dateString) return 'N/A'
const date = new Date(dateString)
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short'
})
}
const handleChangePassword = async () => {
// Validate password match
if (passwordForm.value.new_password !== passwordForm.value.confirm_password) {
......
......@@ -377,7 +377,7 @@
{{ getHistoryItemTitle(item) }}
</p>
<p class="text-xs text-gray-500 dark:text-dark-400">
{{ formatDate(item.used_at) }}
{{ formatDateTime(item.used_at) }}
</p>
</div>
</div>
......@@ -447,6 +447,7 @@ import { useAuthStore } from '@/stores/auth'
import { useAppStore } from '@/stores/app'
import { redeemAPI, authAPI, type RedeemHistoryItem } from '@/api'
import AppLayout from '@/components/layout/AppLayout.vue'
import { formatDateTime } from '@/utils/format'
const { t } = useI18n()
const authStore = useAuthStore()
......@@ -472,18 +473,6 @@ const history = ref<RedeemHistoryItem[]>([])
const loadingHistory = ref(false)
const contactInfo = ref('')
const formatDate = (dateString: string) => {
if (!dateString) return '-'
const date = new Date(dateString)
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}
// Helper functions for history display
const isBalanceType = (type: string) => {
return type === 'balance' || type === 'admin_balance'
......
......@@ -257,6 +257,7 @@ import { useAppStore } from '@/stores/app'
import subscriptionsAPI from '@/api/subscriptions'
import type { UserSubscription } from '@/types'
import AppLayout from '@/components/layout/AppLayout.vue'
import { formatDateOnly } from '@/utils/format'
const { t } = useI18n()
const appStore = useAppStore()
......@@ -300,11 +301,7 @@ function formatExpirationDate(expiresAt: string): string {
return t('userSubscriptions.status.expired')
}
const dateStr = expires.toLocaleDateString(undefined, {
year: 'numeric',
month: 'short',
day: 'numeric'
})
const dateStr = formatDateOnly(expires)
if (days === 0) {
return `${dateStr} (Today)`
......
<template>
<AppLayout>
<div class="space-y-6">
<!-- Summary Stats Cards -->
<TablePageLayout>
<template #actions>
<div class="grid grid-cols-2 gap-4 lg:grid-cols-4">
<!-- Total Requests -->
<div class="card p-4">
......@@ -132,8 +132,9 @@
</div>
</div>
</div>
</template>
<!-- Filters -->
<template #filters>
<div class="card">
<div class="px-6 py-4">
<div class="flex flex-wrap items-end gap-4">
......@@ -170,10 +171,16 @@
</div>
</div>
</div>
</template>
<!-- Usage Table -->
<div class="card overflow-hidden">
<template #table>
<DataTable :columns="columns" :data="usageLogs" :loading="loading">
<template #cell-api_key="{ row }">
<span class="text-sm text-gray-900 dark:text-white">{{
row.api_key?.name || '-'
}}</span>
</template>
<template #cell-model="{ value }">
<span class="font-medium text-gray-900 dark:text-white">{{ value }}</span>
</template>
......@@ -379,9 +386,9 @@
<EmptyState :message="t('usage.noRecords')" />
</template>
</DataTable>
</div>
</template>
<!-- Pagination -->
<template #pagination>
<Pagination
v-if="pagination.total > 0"
:page="pagination.page"
......@@ -389,7 +396,8 @@
:page-size="pagination.page_size"
@update:page="handlePageChange"
/>
</div>
</template>
</TablePageLayout>
</AppLayout>
</template>
......@@ -399,6 +407,7 @@ import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
import { usageAPI, keysAPI } from '@/api'
import AppLayout from '@/components/layout/AppLayout.vue'
import TablePageLayout from '@/components/layout/TablePageLayout.vue'
import DataTable from '@/components/common/DataTable.vue'
import Pagination from '@/components/common/Pagination.vue'
import EmptyState from '@/components/common/EmptyState.vue'
......@@ -406,6 +415,7 @@ import Select from '@/components/common/Select.vue'
import DateRangePicker from '@/components/common/DateRangePicker.vue'
import type { UsageLog, ApiKey, UsageQueryParams, UsageStatsResponse } from '@/types'
import type { Column } from '@/components/common/types'
import { formatDateTime } from '@/utils/format'
const { t } = useI18n()
const appStore = useAppStore()
......@@ -414,6 +424,7 @@ const appStore = useAppStore()
const usageStats = ref<UsageStatsResponse | null>(null)
const columns = computed<Column[]>(() => [
{ key: 'api_key', label: t('usage.apiKeyFilter'), sortable: false },
{ key: 'model', label: t('usage.model'), sortable: true },
{ key: 'stream', label: t('usage.type'), sortable: false },
{ key: 'tokens', label: t('usage.tokens'), sortable: false },
......@@ -505,17 +516,6 @@ const formatCacheTokens = (value: number): string => {
return value.toLocaleString()
}
const formatDateTime = (dateString: string): string => {
const date = new Date(dateString)
return date.toLocaleString('en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})
}
const loadUsageLogs = async () => {
loading.value = true
try {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment