Files
tokens-reef/frontend/src/utils/format.ts
IanShaw027 64b52c4383 fix(frontend): 修复前端重构后的样式一致性和功能完整性
## 修复内容

### 1. AccountsView 功能恢复
- 恢复3个缺失的模态框组件:
  - ReAuthAccountModal.vue - 重新授权功能
  - AccountTestModal.vue - 测试连接功能
  - AccountStatsModal.vue - 查看统计功能
- 恢复 handleTest/handleViewStats/handleReAuth 调用模态框
- 修复 UpdateAccountRequest 类型定义(添加 schedulable 字段)

### 2. DashboardView 修复
- 恢复 formatBalance 函数(支持千位分隔符显示)
- 为 UserDashboardStats 添加完整 Props 类型定义
- 为 UserDashboardRecentUsage 添加完整 Props 类型定义
- 优化格式化函数到共享 utils/format.ts

### 3. 类型安全增强
- 修复 UserAttributeOption 索引签名兼容性
- 移除未使用的类型导入
- 所有组件 Props 类型完整

## 验证结果
-  TypeScript 类型检查通过(0 errors)
-  vue-tsc 检查通过(0 errors)
-  所有样式与重构前100%一致
-  所有功能完整恢复

## 影响范围
- AccountsView: 代码行数从974行优化到189行(提升80.6%可维护性)
- DashboardView: 保持组件化同时恢复所有原有功能
- 深色模式支持完整
- 所有颜色方案和 SVG 图标保持一致

Closes #149
2026-01-05 00:38:23 +08:00

184 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 格式化工具函数
* 参考 CRS 项目的 format.js 实现
*/
import { i18n, getLocale } from '@/i18n'
/**
* 格式化相对时间
* @param date 日期字符串或 Date 对象
* @returns 相对时间字符串,如 "5m ago", "2h ago", "3d ago"
*/
export function formatRelativeTime(date: string | Date | null | undefined): string {
if (!date) return i18n.global.t('common.time.never')
const now = new Date()
const past = new Date(date)
const diffMs = now.getTime() - past.getTime()
// 处理未来时间或无效日期
if (diffMs < 0 || isNaN(diffMs)) return i18n.global.t('common.time.never')
const diffSecs = Math.floor(diffMs / 1000)
const diffMins = Math.floor(diffSecs / 60)
const diffHours = Math.floor(diffMins / 60)
const diffDays = Math.floor(diffHours / 24)
if (diffDays > 0) return i18n.global.t('common.time.daysAgo', { n: diffDays })
if (diffHours > 0) return i18n.global.t('common.time.hoursAgo', { n: diffHours })
if (diffMins > 0) return i18n.global.t('common.time.minutesAgo', { n: diffMins })
return i18n.global.t('common.time.justNow')
}
/**
* 格式化数字(支持 K/M/B 单位)
* @param num 数字
* @returns 格式化后的字符串,如 "1.2K", "3.5M"
*/
export function formatNumber(num: number | null | undefined): string {
if (num === null || num === undefined) return '0'
const locale = getLocale()
const absNum = Math.abs(num)
// Use Intl.NumberFormat for compact notation if supported and needed
// Note: Compact notation in 'zh' uses '万/亿', which is appropriate for Chinese
const formatter = new Intl.NumberFormat(locale, {
notation: absNum >= 10000 ? 'compact' : 'standard',
maximumFractionDigits: 1
})
return formatter.format(num)
}
/**
* 格式化货币金额
* @param amount 金额
* @param currency 货币代码,默认 USD
* @returns 格式化后的字符串,如 "$1.25"
*/
export function formatCurrency(amount: number | null | undefined, currency: string = 'USD'): string {
if (amount === null || amount === undefined) return '$0.00'
const locale = getLocale()
// For very small amounts, show more decimals
const fractionDigits = amount > 0 && amount < 0.01 ? 6 : 2
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency,
minimumFractionDigits: fractionDigits,
maximumFractionDigits: fractionDigits
}).format(amount)
}
/**
* 格式化字节大小
* @param bytes 字节数
* @param decimals 小数位数
* @returns 格式化后的字符串,如 "1.5 MB"
*/
export function formatBytes(bytes: number, decimals: number = 2): string {
if (bytes === 0) return '0 Bytes'
const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
}
/**
* 格式化日期
* @param date 日期字符串或 Date 对象
* @param options Intl.DateTimeFormatOptions
* @returns 格式化后的日期字符串
*/
export function formatDate(
date: string | Date | null | undefined,
options: Intl.DateTimeFormatOptions = {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}
): string {
if (!date) return ''
const d = new Date(date)
if (isNaN(d.getTime())) return ''
const locale = getLocale()
return new Intl.DateTimeFormat(locale, options).format(d)
}
/**
* 格式化日期(只显示日期部分)
* @param date 日期字符串或 Date 对象
* @returns 格式化后的日期字符串
*/
export function formatDateOnly(date: string | Date | null | undefined): string {
return formatDate(date, {
year: 'numeric',
month: '2-digit',
day: '2-digit'
})
}
/**
* 格式化日期时间(完整格式)
* @param date 日期字符串或 Date 对象
* @returns 格式化后的日期时间字符串
*/
export function formatDateTime(date: string | Date | null | undefined): string {
return formatDate(date)
}
/**
* 格式化时间(只显示时分)
* @param date 日期字符串或 Date 对象
* @returns 格式化后的时间字符串
*/
export function formatTime(date: string | Date | null | undefined): string {
return formatDate(date, {
hour: '2-digit',
minute: '2-digit',
hour12: false
})
}
/**
* 格式化数字(千分位分隔,不使用紧凑单位)
* @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()
}