优化: 前端DRY收敛与命名对齐

审批等待格式化与阈值沉 utils/time 正典
相对时间阶梯三套收敛 formatRelative 参数化
灵感状态表沉 constants,透传通道退役
aggText/clearOwn 等命名对齐,IPC 参数名统一 conversation_id
This commit is contained in:
lxy
2026-10-01 19:35:20 +08:00
parent 6ef15a0940
commit 0560137840
16 changed files with 111 additions and 97 deletions
+3 -10
View File
@@ -12,6 +12,7 @@ import { useI18n } from 'vue-i18n'
import { useConfirm } from '@/composables/useConfirm'
import { aiApi } from '@/api'
import { getApprovalRemainingMs } from './aiShared'
import { formatWaitTime, waitLevelOf, type WaitLevel } from '@/utils/time'
import type { AiToolCallInfo } from '@/api/types'
/**
@@ -115,17 +116,9 @@ export function useToolApproval(tc: Ref<AiToolCallInfo>, emit: ToolApprovalEmit)
waitSecs.value = 0
}
function formatWaitTime(secs: number): string {
const m = Math.floor(secs / 60)
const s = secs % 60
return `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
}
// formatWaitTime/waitLevelOf 收敛至 @/utils/time(原与 ApprovalPopup 逐字双写)
const waitLevel = computed<'normal' | 'long' | 'urgent'>(() => {
if (waitSecs.value >= 300) return 'urgent' // 5分钟
if (waitSecs.value >= 120) return 'long' // 2分钟
return 'normal'
})
const waitLevel = computed<WaitLevel>(() => waitLevelOf(waitSecs.value))
// ── 审批到期倒计时(BUG-260624-02)──
// 剩余 = aiShared 审批超时计时器真实起点推算(自动拒绝时刻精确对齐,非本地近似)。
+1 -1
View File
@@ -96,7 +96,7 @@ export function statsTail(s: TurnCallStats): string {
* 头部 total=0 显「过程记录」;零值段省略;durationMs 可选(轨迹卡传起止差,分组行不传)。
* 供 MessageList 轨迹卡与 ToolCardList 聚合分组行共用,拼装语义单一出口。
*/
export function aggText(s: TurnCallStats, durationMs?: number): string {
export function turnSummaryText(s: TurnCallStats, durationMs?: number): string {
const parts: string[] = []
parts.push(s.total > 0 ? t('ai.trace.calls', { n: s.total }) : t('ai.trace.processOnly'))
const tail = statsTail(s)
+3 -3
View File
@@ -16,7 +16,7 @@ export function useTimerOwnership() {
const timeouts = new Set<ReturnType<typeof setTimeout>>()
const intervals = new Set<ReturnType<typeof setInterval>>()
/** 注册一次性 timeout;触发后自动从注册表移除。返回可交给 clearOwn 的 id。 */
/** 注册一次性 timeout;触发后自动从注册表移除。返回可交给 clearOwnTimeout 的 id。 */
function setOwnTimeout(fn: () => void, ms: number) {
const id = setTimeout(() => {
timeouts.delete(id)
@@ -34,7 +34,7 @@ export function useTimerOwnership() {
}
/** 清理指定 timeout(id 为 null/undefined 时 no-op)。 */
function clearOwn(id?: ReturnType<typeof setTimeout> | null) {
function clearOwnTimeout(id?: ReturnType<typeof setTimeout> | null) {
if (id != null) {
clearTimeout(id)
timeouts.delete(id)
@@ -56,5 +56,5 @@ export function useTimerOwnership() {
intervals.clear()
})
return { setOwnTimeout, setOwnInterval, clearOwn, clearOwnInterval }
return { setOwnTimeout, setOwnInterval, clearOwnTimeout, clearOwnInterval }
}
+3 -3
View File
@@ -34,14 +34,14 @@ export function useToast(defaultDurationMs = 3000) {
// 会清掉组件 A 的 timer,致 A 的 toast 永不隐藏(跨实例 bug)。
// 已下沉为「每实例独立 timer」:useTimerOwnership 每实例注册表 + onUnmounted 只清自己。
// 不再保留模块级兜底 —— 模块级共享正是串扰根源,每实例自清理即正确语义。
const { setOwnTimeout, clearOwn } = useTimerOwnership()
const { setOwnTimeout, clearOwnTimeout } = useTimerOwnership()
let _timer: ReturnType<typeof setTimeout> | null = null
function showToast(msg: string, type: ToastType = 'info', durationMs?: number) {
toast.msg = msg
toast.type = type
toast.visible = true
if (_timer) clearOwn(_timer)
if (_timer) clearOwnTimeout(_timer)
_timer = setOwnTimeout(() => {
_timer = null
toast.visible = false
@@ -50,7 +50,7 @@ export function useToast(defaultDurationMs = 3000) {
function hideToast() {
if (_timer) {
clearOwn(_timer)
clearOwnTimeout(_timer)
_timer = null
}
toast.visible = false