diff --git a/frontend/src/components/admin/account/AccountStatsModal.vue b/frontend/src/components/admin/account/AccountStatsModal.vue new file mode 100644 index 00000000..93f38a83 --- /dev/null +++ b/frontend/src/components/admin/account/AccountStatsModal.vue @@ -0,0 +1,783 @@ + + + diff --git a/frontend/src/components/admin/account/AccountTestModal.vue b/frontend/src/components/admin/account/AccountTestModal.vue new file mode 100644 index 00000000..619a2ba3 --- /dev/null +++ b/frontend/src/components/admin/account/AccountTestModal.vue @@ -0,0 +1,510 @@ + + + diff --git a/frontend/src/components/admin/account/ReAuthAccountModal.vue b/frontend/src/components/admin/account/ReAuthAccountModal.vue new file mode 100644 index 00000000..9bfa9530 --- /dev/null +++ b/frontend/src/components/admin/account/ReAuthAccountModal.vue @@ -0,0 +1,651 @@ + + + diff --git a/frontend/src/components/user/dashboard/UserDashboardCharts.vue b/frontend/src/components/user/dashboard/UserDashboardCharts.vue index a50b738a..39e8bb30 100644 --- a/frontend/src/components/user/dashboard/UserDashboardCharts.vue +++ b/frontend/src/components/user/dashboard/UserDashboardCharts.vue @@ -1,31 +1,151 @@ diff --git a/frontend/src/components/user/dashboard/UserDashboardQuickActions.vue b/frontend/src/components/user/dashboard/UserDashboardQuickActions.vue index 4b4e9efa..83180025 100644 --- a/frontend/src/components/user/dashboard/UserDashboardQuickActions.vue +++ b/frontend/src/components/user/dashboard/UserDashboardQuickActions.vue @@ -1,15 +1,60 @@ \ No newline at end of file diff --git a/frontend/src/components/user/dashboard/UserDashboardRecentUsage.vue b/frontend/src/components/user/dashboard/UserDashboardRecentUsage.vue index 9246fa15..56f361bb 100644 --- a/frontend/src/components/user/dashboard/UserDashboardRecentUsage.vue +++ b/frontend/src/components/user/dashboard/UserDashboardRecentUsage.vue @@ -1,18 +1,60 @@ \ No newline at end of file +import { useI18n } from 'vue-i18n' +import LoadingSpinner from '@/components/common/LoadingSpinner.vue' +import EmptyState from '@/components/common/EmptyState.vue' +import { formatDateTime } from '@/utils/format' +import type { UsageLog } from '@/types' + +defineProps<{ + data: UsageLog[] + loading: boolean +}>() +const { t } = useI18n() +const formatCost = (c: number) => c.toFixed(4) + diff --git a/frontend/src/components/user/dashboard/UserDashboardStats.vue b/frontend/src/components/user/dashboard/UserDashboardStats.vue index 7b30f728..6cf7e07f 100644 --- a/frontend/src/components/user/dashboard/UserDashboardStats.vue +++ b/frontend/src/components/user/dashboard/UserDashboardStats.vue @@ -1,24 +1,171 @@ \ No newline at end of file +import { useI18n } from 'vue-i18n' +import type { UserDashboardStats as UserStatsType } from '@/api/usage' + +defineProps<{ + stats: UserStatsType + balance: number + isSimple: boolean +}>() +const { t } = useI18n() + +const formatBalance = (b: number) => + new Intl.NumberFormat('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }).format(b) + +const formatNumber = (n: number) => n.toLocaleString() +const formatCost = (c: number) => c.toFixed(4) +const formatTokens = (t: number) => (t >= 1000 ? `${(t / 1000).toFixed(1)}K` : t.toString()) +const formatDuration = (ms: number) => ms >= 1000 ? `${(ms / 1000).toFixed(2)}s` : `${ms.toFixed(0)}ms` + diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index f5e2f23c..447ed77a 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -496,6 +496,7 @@ export interface UpdateAccountRequest { proxy_id?: number | null concurrency?: number priority?: number + schedulable?: boolean status?: 'active' | 'inactive' group_ids?: number[] confirm_mixed_channel_risk?: boolean @@ -846,6 +847,7 @@ export type UserAttributeType = 'text' | 'textarea' | 'number' | 'email' | 'url' export interface UserAttributeOption { value: string label: string + [key: string]: unknown } export interface UserAttributeValidation { @@ -910,4 +912,4 @@ export interface UpdateUserAttributeRequest { export interface UserAttributeValuesMap { [attributeId: number]: string -} \ No newline at end of file +} diff --git a/frontend/src/utils/format.ts b/frontend/src/utils/format.ts index 5689fd35..d54e5015 100644 --- a/frontend/src/utils/format.ts +++ b/frontend/src/utils/format.ts @@ -152,4 +152,32 @@ export function formatTime(date: string | Date | null | undefined): string { minute: '2-digit', hour12: false }) -} \ No newline at end of file +} + +/** + * 格式化数字(千分位分隔,不使用紧凑单位) + * @param num 数字 + * @returns 格式化后的字符串,如 "12,345" + */ +export function formatNumberLocaleString(num: number): string { + return num.toLocaleString() +} + +/** + * 格式化金额(固定小数位,不带货币符号) + * @param amount 金额 + * @param fractionDigits 小数位数,默认 4 + * @returns 格式化后的字符串,如 "1.2345" + */ +export function formatCostFixed(amount: number, fractionDigits: number = 4): string { + return amount.toFixed(fractionDigits) +} + +/** + * 格式化 token 数量(>=1000 显示为 K,保留 1 位小数) + * @param tokens token 数量 + * @returns 格式化后的字符串,如 "950", "1.2K" + */ +export function formatTokensK(tokens: number): string { + return tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}K` : tokens.toString() +} diff --git a/frontend/src/views/admin/AccountsView.vue b/frontend/src/views/admin/AccountsView.vue index a727bdfb..be12ac83 100644 --- a/frontend/src/views/admin/AccountsView.vue +++ b/frontend/src/views/admin/AccountsView.vue @@ -4,56 +4,186 @@ - + + + + + + \ No newline at end of file + diff --git a/frontend/src/views/admin/UsersView.vue b/frontend/src/views/admin/UsersView.vue index f5ce601a..6e896ab9 100644 --- a/frontend/src/views/admin/UsersView.vue +++ b/frontend/src/views/admin/UsersView.vue @@ -1,52 +1,562 @@ diff --git a/frontend/src/views/user/DashboardView.vue b/frontend/src/views/user/DashboardView.vue index ef406bea..39d2f877 100644 --- a/frontend/src/views/user/DashboardView.vue +++ b/frontend/src/views/user/DashboardView.vue @@ -1,13 +1,13 @@