优化: aichat 体验收尾(useAiEvents拆分4子域 + AIC-FIX P1队列/mutex/缓存 + i18n残留抽取)
This commit is contained in:
@@ -114,7 +114,8 @@ import { ref, computed, nextTick, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAiStore } from '../../stores/ai'
|
||||
import { useProjectStore } from '../../stores/project'
|
||||
import { getConvState, textIdle } from '../../composables/ai/useAiEvents'
|
||||
import { getConvState } from '../../composables/ai/useAiEvents'
|
||||
import { textIdle } from '../../composables/ai/useAiPendingState'
|
||||
import { extractImageUrlParts } from '../../composables/ai/utils'
|
||||
import { uploadFile } from '../../composables/ai/fileUpload'
|
||||
import { open } from '@tauri-apps/plugin-dialog'
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
import { reactive, computed, watch, nextTick, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAiStore } from '../../stores/ai'
|
||||
import { pendingDirAuths } from '../../composables/ai/useAiEvents'
|
||||
import { pendingDirAuths } from '../../composables/ai/useAiPendingState'
|
||||
import { aiApi } from '../../api'
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
import { computed, ref, watch, nextTick } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAiStore } from '../../stores/ai'
|
||||
import { pendingHelp } from '../../composables/ai/useAiEvents'
|
||||
import { pendingHelp } from '../../composables/ai/useAiPendingState'
|
||||
|
||||
// toast 经 emit 转父(保持单一 toast 源,与 MaxRoundsCard 一致)
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
import { ref, computed, watch, nextTick } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAiStore } from '../../stores/ai'
|
||||
import { pendingMaxRounds, getConvState } from '../../composables/ai/useAiEvents'
|
||||
import { getConvState } from '../../composables/ai/useAiEvents'
|
||||
import { pendingMaxRounds } from '../../composables/ai/useAiPendingState'
|
||||
import { aiApi } from '../../api'
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
||||
@@ -901,7 +901,7 @@ defineExpose({
|
||||
:aria-expanded="tokenPopoverMsgId === item.msg.id"
|
||||
@click="toggleTokenPopover(item.msg, $event)"
|
||||
>
|
||||
<span>{{ formatTokens(tokenInOf(item.msg)) }} in<template v-if="tokenEstimated(item.msg)"><span class="ai-token-est">(估算)</span></template></span>
|
||||
<span>{{ formatTokens(tokenInOf(item.msg)) }} in<template v-if="tokenEstimated(item.msg)"><span class="ai-token-est">({{ $t('aiChat.estimated') }})</span></template></span>
|
||||
<span class="ai-token-sep">·</span>
|
||||
<span>{{ formatTokens(tokenCacheOf(item.msg)) }} cache</span>
|
||||
<span class="ai-token-sep">·</span>
|
||||
@@ -917,34 +917,34 @@ defineExpose({
|
||||
class="ai-token-popover"
|
||||
@click.stop
|
||||
>
|
||||
<div class="ai-token-popover-title">Token 用量详情<template v-if="tokenEstimated(item.msg)"><span class="ai-token-est">(估算)</span></template></div>
|
||||
<div class="ai-token-popover-title">{{ $t('aiChat.tokenDetailTitle') }}<template v-if="tokenEstimated(item.msg)"><span class="ai-token-est">({{ $t('aiChat.estimated') }})</span></template></div>
|
||||
<div class="ai-token-popover-row">
|
||||
<span class="ai-token-popover-label">输入(未命中,全价)</span>
|
||||
<span class="ai-token-popover-label">{{ $t('aiChat.tokenInLabel') }}</span>
|
||||
<span class="ai-token-popover-val">{{ formatTokens(tokenInOf(item.msg)) }} ({{ tokenInOf(item.msg) }})</span>
|
||||
</div>
|
||||
<div class="ai-token-popover-row">
|
||||
<span class="ai-token-popover-label">缓存命中(低价)</span>
|
||||
<span class="ai-token-popover-label">{{ $t('aiChat.tokenCacheLabel') }}</span>
|
||||
<span class="ai-token-popover-val">{{ formatTokens(tokenCacheOf(item.msg)) }} ({{ tokenCacheOf(item.msg) }})</span>
|
||||
</div>
|
||||
<div class="ai-token-popover-row">
|
||||
<span class="ai-token-popover-label">输出</span>
|
||||
<span class="ai-token-popover-label">{{ $t('aiChat.tokenOutLabel') }}</span>
|
||||
<span class="ai-token-popover-val">{{ formatTokens(tokenOutOf(item.msg)) }} ({{ tokenOutOf(item.msg) }})</span>
|
||||
</div>
|
||||
<div v-if="tokenReasonOf(item.msg) > 0" class="ai-token-popover-row">
|
||||
<span class="ai-token-popover-label">思考(reasoning)</span>
|
||||
<span class="ai-token-popover-label">{{ $t('aiChat.tokenReasonLabel') }}</span>
|
||||
<span class="ai-token-popover-val">{{ formatTokens(tokenReasonOf(item.msg)) }} ({{ tokenReasonOf(item.msg) }})</span>
|
||||
</div>
|
||||
<div v-if="cacheHitRate(item.msg) != null" class="ai-token-popover-row">
|
||||
<span class="ai-token-popover-label">缓存命中率</span>
|
||||
<span class="ai-token-popover-label">{{ $t('aiChat.tokenCacheRateLabel') }}</span>
|
||||
<span class="ai-token-popover-val">{{ cacheHitRate(item.msg) }}%</span>
|
||||
</div>
|
||||
<div v-if="item.msg.model" class="ai-token-popover-row">
|
||||
<span class="ai-token-popover-label">模型</span>
|
||||
<span class="ai-token-popover-label">{{ $t('aiChat.tokenModelLabel') }}</span>
|
||||
<span class="ai-token-popover-val">{{ item.msg.model }}</span>
|
||||
</div>
|
||||
<div v-if="tokenEstimated(item.msg)" class="ai-token-popover-row">
|
||||
<span class="ai-token-popover-label">用量说明</span>
|
||||
<span class="ai-token-popover-val">prompt_tokens 缺失,按估算展示</span>
|
||||
<span class="ai-token-popover-label">{{ $t('aiChat.tokenUsageNoteLabel') }}</span>
|
||||
<span class="ai-token-popover-val">{{ $t('aiChat.tokenUsageNote') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -172,8 +172,8 @@
|
||||
<div class="ai-context-section-title">{{ $t('aiChat.contextSystemPrompt') }}</div>
|
||||
<div class="ai-context-section-body">
|
||||
<div class="ai-context-row">
|
||||
<span class="ai-context-label">目标({{ contextInfo.goals.length }})</span>
|
||||
<span class="ai-context-value">{{ contextInfo.goals.join('; ') || '无' }}</span>
|
||||
<span class="ai-context-label">{{ $t('aiChat.goalsCount', { n: contextInfo.goals.length }) }}</span>
|
||||
<span class="ai-context-value">{{ contextInfo.goals.join('; ') || $t('aiChat.none') }}</span>
|
||||
</div>
|
||||
<div class="ai-context-row">
|
||||
<span class="ai-context-label">{{ $t('aiChat.contextEnvironment') }}</span>
|
||||
@@ -190,8 +190,8 @@
|
||||
<div class="ai-context-section-title">{{ $t('aiChat.contextEnrichment') }}</div>
|
||||
<div class="ai-context-section-body">
|
||||
<div class="ai-context-row">
|
||||
<span class="ai-context-label">@项目</span>
|
||||
<span class="ai-context-value">{{ contextInfo.projectNames || '无' }}</span>
|
||||
<span class="ai-context-label">{{ $t('aiChat.projectLabel') }}</span>
|
||||
<span class="ai-context-value">{{ contextInfo.projectNames || $t('aiChat.none') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -325,9 +325,13 @@ function onModelSelect(e: Event) {
|
||||
store.modelOverride.value = v || null
|
||||
}
|
||||
|
||||
// 切换会话默认选中权重最高 model
|
||||
// 切换会话:仅当手选模型空/对目标模型池无效时才回落权重最高(保留用户手选,不每次重置)
|
||||
watch(() => store.state.activeConversationId, () => {
|
||||
store.modelOverride.value = enabledModels.value[0]?.model_id || null
|
||||
const current = store.modelOverride.value
|
||||
const valid = current && enabledModels.value.some(m => m.model_id === current)
|
||||
if (!valid) {
|
||||
store.modelOverride.value = enabledModels.value[0]?.model_id || null
|
||||
}
|
||||
})
|
||||
|
||||
// 切 provider 后模型 override 同步(BUG-2026-07-07:provider 切换后 header 模型名不刷新):
|
||||
@@ -372,7 +376,7 @@ function fmtTime(ts: number): string {
|
||||
const yest = new Date(now)
|
||||
yest.setDate(yest.getDate() - 1)
|
||||
if (d.getFullYear() === yest.getFullYear() && d.getMonth() === yest.getMonth() && d.getDate() === yest.getDate()) {
|
||||
return `昨天 ${hm}`
|
||||
return `${t('aiChat.yesterday')} ${hm}`
|
||||
}
|
||||
// 更早
|
||||
return `${pad(d.getMonth()+1)}-${pad(d.getDate())} ${hm}`
|
||||
@@ -481,7 +485,7 @@ const contextInfo = computed<ContextInfo>(() => {
|
||||
goals: conv?.pinned_goals ?? [],
|
||||
summaryCount: summaryMsgs.value.length,
|
||||
osInfo: 'Windows 11 / PowerShell',
|
||||
projectNames: Array.from(projectLabels).join(', ') || '无',
|
||||
projectNames: Array.from(projectLabels).join(', ') || t('aiChat.none'),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user