diff --git a/frontend/src/components/RelativeTime.vue b/frontend/src/components/RelativeTime.vue
new file mode 100644
index 0000000..7a8411d
--- /dev/null
+++ b/frontend/src/components/RelativeTime.vue
@@ -0,0 +1,65 @@
+
+
+ {{ displayText }}
+
+
+
+
+
+
diff --git a/frontend/src/utils/time.ts b/frontend/src/utils/time.ts
new file mode 100644
index 0000000..0acf99c
--- /dev/null
+++ b/frontend/src/utils/time.ts
@@ -0,0 +1,110 @@
+/**
+ * 相对时间工具函数
+ * 将时间字符串转换为相对时间显示文本
+ */
+
+export interface RelativeTimeResult {
+ /** 相对时间显示文本 */
+ displayText: string
+ /** 完整时间字符串(hover显示) */
+ fullTime: string
+ /** 是否需要自动刷新(10分钟内) */
+ needRefresh: boolean
+}
+
+/**
+ * 格式化相对时间
+ * @param timeStr 时间字符串,支持格式:'yyyy-MM-dd HH:mm:ss' | Date | number(时间戳)
+ * @returns 相对时间结果
+ */
+export function formatRelativeTime(timeStr: string | Date | number | null | undefined): RelativeTimeResult {
+ if (!timeStr) {
+ return { displayText: '--', fullTime: '--', needRefresh: false }
+ }
+
+ let date: Date
+ if (timeStr instanceof Date) {
+ date = timeStr
+ } else if (typeof timeStr === 'number') {
+ date = new Date(timeStr)
+ } else {
+ date = new Date(timeStr.replace(/-/g, '/')) // 兼容iOS Safari的日期解析
+ }
+
+ if (isNaN(date.getTime())) {
+ return { displayText: '--', fullTime: String(timeStr), needRefresh: false }
+ }
+
+ const now = Date.now()
+ const diff = now - date.getTime()
+ const absDiff = Math.abs(diff)
+
+ // 完整时间
+ const fullTime = formatFullTime(date)
+
+ // 未来时间直接返回完整时间
+ if (diff < -60000) {
+ return { displayText: fullTime, fullTime, needRefresh: false }
+ }
+
+ // 10秒内
+ if (absDiff < 10 * 1000) {
+ return { displayText: '刚刚', fullTime, needRefresh: true }
+ }
+
+ // 1分钟内
+ if (absDiff < 60 * 1000) {
+ const seconds = Math.floor(absDiff / 1000)
+ return { displayText: `${seconds}秒前`, fullTime, needRefresh: true }
+ }
+
+ // 1小时内
+ if (absDiff < 60 * 60 * 1000) {
+ const minutes = Math.floor(absDiff / (60 * 1000))
+ return { displayText: `${minutes}分钟前`, fullTime, needRefresh: true }
+ }
+
+ // 24小时内
+ if (absDiff < 24 * 60 * 60 * 1000) {
+ const hours = Math.floor(absDiff / (60 * 60 * 1000))
+ const minutes = Math.floor((absDiff % (60 * 60 * 1000)) / (60 * 1000))
+ if (minutes > 0) {
+ return { displayText: `${hours}小时${minutes}分钟前`, fullTime, needRefresh: true }
+ }
+ return { displayText: `${hours}小时前`, fullTime, needRefresh: true }
+ }
+
+ // 2天内(昨天/前天)
+ if (absDiff < 2 * 24 * 60 * 60 * 1000) {
+ return { displayText: '1天前', fullTime, needRefresh: false }
+ }
+
+ // 7天内
+ if (absDiff < 7 * 24 * 60 * 60 * 1000) {
+ const days = Math.floor(absDiff / (24 * 60 * 60 * 1000))
+ return { displayText: `${days}天前`, fullTime, needRefresh: false }
+ }
+
+ // 超过7天:显示 MM-DD HH:mm
+ return { displayText: formatShortDate(date), fullTime, needRefresh: false }
+}
+
+/** 格式化完整时间 yyyy-MM-dd HH:mm:ss */
+function formatFullTime(date: Date): string {
+ const y = date.getFullYear()
+ const m = String(date.getMonth() + 1).padStart(2, '0')
+ const d = String(date.getDate()).padStart(2, '0')
+ const h = String(date.getHours()).padStart(2, '0')
+ const min = String(date.getMinutes()).padStart(2, '0')
+ const s = String(date.getSeconds()).padStart(2, '0')
+ return `${y}-${m}-${d} ${h}:${min}:${s}`
+}
+
+/** 格式化短日期 MM-DD HH:mm */
+function formatShortDate(date: Date): string {
+ const m = String(date.getMonth() + 1).padStart(2, '0')
+ const d = String(date.getDate()).padStart(2, '0')
+ const h = String(date.getHours()).padStart(2, '0')
+ const min = String(date.getMinutes()).padStart(2, '0')
+ return `${m}-${d} ${h}:${min}`
+}