修复: 审批不超时+重启恢复 + 其他小改

- APPROVAL_TIMEOUT_MS=Infinity(已决策:审批不做超时)
- V33 迁移: ai_conversations 加 pending_approvals 列
- save_conversation 持久化 pending_approvals
- ai_conversation_switch 从 DB 恢复 pending_approvals
- switchConversation 加 try/catch(对话不存在→建新)
- @项目 关联添加取消按钮(×)
- TopBar 底部无标题时图标右对齐
- TaskDetail.vue 删除重复 case
This commit is contained in:
2026-06-28 13:09:55 +08:00
parent c4b02b5370
commit ad1821bc14
11 changed files with 140 additions and 19 deletions

View File

@@ -134,6 +134,7 @@
</svg>
<span class="ai-enrichment-badge">{{ enrichmentBadgeText }}</span>
</button>
<button class="ai-enrichment-dismiss" @click="dismissEnrichment" title="取消关联">×</button>
<div v-if="enrichmentExpanded" class="ai-enrichment-panel">
<div class="ai-enrichment-label">{{ $t('aiChat.enrichmentLabel') }}</div>
<pre class="ai-enrichment-body"><code>{{ enrichmentDetailText }}</code></pre>
@@ -485,6 +486,28 @@ const enrichmentDetailText = computed(() => {
return lines.join('\n')
})
/** 取消 @项目 关联:清除 mentionSpan + 移除输入框中的 [@项目: xxx] 标记 */
function dismissEnrichment() {
const projectSpans = pendingMentionSpans.value.filter(sp => sp.kind === 'project')
if (projectSpans.length === 0) return
// 从 inputText 中移除所有 [@项目: xxx] 标记
let text = inputText.value
for (const span of projectSpans) {
const label = text.slice(span.start, span.start + span.length)
// 替换标记 + 前后可能的空格为单个空格
const pattern = new RegExp(`\\s*${escapeRegex(label)}\s*`)
text = text.replace(pattern, ' ').trim()
}
inputText.value = text
// 清除 project 类型的 mentionSpan
pendingMentionSpans.value = pendingMentionSpans.value.filter(sp => sp.kind !== 'project')
enrichmentExpanded.value = false
}
function escapeRegex(s: string): string {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
const mentionGroupLabel = computed(() => ({
project: t('aiChat.mentionGroupProject'),
task: t('aiChat.mentionGroupTask'),
@@ -1078,6 +1101,8 @@ defineExpose({
/* ── ⑥.4 Phase 4: @项目展开摘要(输入区 enrichment 预览) ── */
.ai-enrichment-bar {
display: flex;
align-items: center;
margin-top: 6px;
border: 0.5px solid color-mix(in srgb, var(--df-accent) 30%, transparent);
border-radius: var(--df-radius);
@@ -1111,6 +1136,20 @@ defineExpose({
.ai-enrichment-toggle--expanded .ai-enrichment-chevron {
transform: rotate(180deg);
}
.ai-enrichment-dismiss {
flex-shrink: 0;
border: none;
background: transparent;
color: var(--df-text-dim);
cursor: pointer;
font-size: 14px;
padding: 0 4px;
line-height: 1;
transition: color 0.15s;
}
.ai-enrichment-dismiss:hover {
color: var(--df-danger);
}
.ai-enrichment-badge {
flex: 1;
min-width: 0;

View File

@@ -538,26 +538,26 @@ onBeforeUnmount(() => {
.ai-bottom-tools {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 14px 4px;
}
.ai-bottom-title {
font-size: 11px;
color: var(--df-text-secondary);
min-width: 0;
flex: 1;
margin-right: 8px;
margin-right: auto;
}
.ai-bottom-tools-right {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
margin-left: auto;
}
/* ═══ Provider Bar (header 中间区) ═══ */
.ai-provider-bar {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 11px;
color: var(--df-text-secondary);