重构: 前端 composable 拆分+详情页 Markdown 渲染+流式 memo+状态同步+bug 修复
- composables/useMarkdown 抽(AiChat 复用)+ai/* 流式核心+useAiSend catch 回滚 user msg(B-21)+useAiStream 超时文案(B-03)+useAiConversations 切换 token(FR-R1) - views/TaskDetail 新建(F-02)+描述 md+mdReady 响应式+字段同行布局(B-24/31) - views/ProjectDetail 多选审批 UI(F-01)+描述 md+mdReady+字段同行(B-25/31) - views/Ideas+Knowledge 描述 md+mdReady(B-25) - views/Tasks 项目切换联动(B-29)+task 详情跳转 - components/AiChat toast 失败提示(B-20)+流式块级 memo(ARC-08)+clean UI(AR-7)+stop 兜底(AR-5) - components/ToolCard 审批卡片可读化(AR-3) - stores useAiStore barrel(ARC-04)+del settings mock(ARC-01)+approve options(R-PD-5) - i18n aiTool/nav 中英对称(AR-3/9)+删 nav.decisions 死链(ARC-02) - router /tasks/:id(F-02)+global.css selection 可见(R-260615-01)+api task detail+App 删 decisions nav
This commit is contained in:
@@ -59,7 +59,13 @@
|
||||
<h2 class="detail-title">{{ currentIdea.title }}</h2>
|
||||
<span class="status-tag" :class="'status-' + currentIdea.status">{{ $t(statusLabelKey(currentIdea.status)) }}</span>
|
||||
</div>
|
||||
<p class="detail-desc">{{ currentIdea.description }}</p>
|
||||
<!-- B-260615-25:灵感描述 Markdown 渲染,复用 useMarkdown composable(同 B-24 TaskDetail),空值回退 — -->
|
||||
<p
|
||||
v-if="currentIdea.description"
|
||||
class="detail-desc ai-md"
|
||||
v-html="renderedDesc"
|
||||
></p>
|
||||
<p v-else class="detail-desc">—</p>
|
||||
|
||||
<!-- 对抗式评估 -->
|
||||
<div class="detail-section">
|
||||
@@ -208,8 +214,19 @@ import { useProjectStore } from '../stores/project'
|
||||
import { formatDate } from '../utils/time'
|
||||
import ConfirmDialog from '../components/ConfirmDialog.vue'
|
||||
import { useConfirm } from '../composables/useConfirm'
|
||||
import { useMarkdown } from '../composables/useMarkdown'
|
||||
import type { IdeaRecord } from '../api/types'
|
||||
|
||||
// B-260615-25:灵感描述 Markdown 渲染(复用 AiChat/TaskDetail 同款渲染器,模块级单例)
|
||||
const { renderMd, loadMarkdown, mdReady } = useMarkdown()
|
||||
|
||||
// 描述渲染依赖 mdReady 响应式:loadMarkdown 完成前 escapeFallback 纯文本,完成后 mdReady 翻转
|
||||
// 触发 computed 重算(B-25 漏响应式致 v-html=renderMd 首次纯文本后不重算,代码块不渲染)
|
||||
const renderedDesc = computed(() => {
|
||||
void mdReady.value
|
||||
return renderMd(currentIdea.value?.description ?? '')
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const store = useProjectStore()
|
||||
@@ -433,6 +450,7 @@ async function onStatusChange(e: Event) {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
loadMarkdown() // 后台预热 Markdown 渲染器(模块单例,与 AiChat/TaskDetail 共享),不阻塞
|
||||
await store.loadIdeas()
|
||||
})
|
||||
</script>
|
||||
@@ -797,6 +815,63 @@ onMounted(async () => {
|
||||
margin-bottom: var(--df-gap-page);
|
||||
}
|
||||
|
||||
/* ===== 灵感描述 Markdown 渲染(B-260615-25,样式同 TaskDetail B-24 .ai-md 收敛) ===== */
|
||||
.detail-desc.ai-md { white-space: normal; color: var(--df-text-secondary); }
|
||||
.detail-desc.ai-md :deep(p) { margin: 0 0 6px; }
|
||||
.detail-desc.ai-md :deep(p:last-child) { margin-bottom: 0; }
|
||||
.detail-desc.ai-md :deep(ul), .detail-desc.ai-md :deep(ol) { margin: 4px 0; padding-left: 20px; }
|
||||
.detail-desc.ai-md :deep(li) { margin: 2px 0; line-height: 1.5; }
|
||||
.detail-desc.ai-md :deep(code) {
|
||||
font-family: var(--df-font-mono);
|
||||
font-size: 12px;
|
||||
padding: 1px 5px;
|
||||
background: rgba(255,255,255,0.06);
|
||||
border-radius: var(--df-radius-sm);
|
||||
color: var(--df-accent);
|
||||
}
|
||||
.detail-desc.ai-md :deep(pre) {
|
||||
margin: 8px 0;
|
||||
padding: 10px 12px;
|
||||
background: var(--df-bg);
|
||||
border: 0.5px solid var(--df-border);
|
||||
border-radius: var(--df-radius);
|
||||
overflow-x: auto;
|
||||
}
|
||||
.detail-desc.ai-md :deep(pre code) {
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
color: var(--df-text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.detail-desc.ai-md :deep(blockquote) {
|
||||
margin: 6px 0;
|
||||
padding: 4px 12px;
|
||||
border-left: 2px solid var(--df-accent);
|
||||
color: var(--df-text-secondary);
|
||||
}
|
||||
.detail-desc.ai-md :deep(h1), .detail-desc.ai-md :deep(h2), .detail-desc.ai-md :deep(h3) {
|
||||
font-weight: 500;
|
||||
color: var(--df-text);
|
||||
margin: 8px 0 4px;
|
||||
}
|
||||
.detail-desc.ai-md :deep(h1) { font-size: 16px; }
|
||||
.detail-desc.ai-md :deep(h2) { font-size: 14px; }
|
||||
.detail-desc.ai-md :deep(h3) { font-size: 13px; }
|
||||
.detail-desc.ai-md :deep(a) { color: var(--df-accent); text-decoration: none; }
|
||||
.detail-desc.ai-md :deep(a:hover) { text-decoration: underline; }
|
||||
.detail-desc.ai-md :deep(strong) { font-weight: 500; color: var(--df-text); }
|
||||
.detail-desc.ai-md :deep(hr) { border: none; border-top: 0.5px solid var(--df-border); margin: 8px 0; }
|
||||
.detail-desc.ai-md :deep(table) {
|
||||
width: 100%; border-collapse: collapse; margin: 6px 0;
|
||||
font-size: 12px; overflow-x: auto; display: block;
|
||||
}
|
||||
.detail-desc.ai-md :deep(th), .detail-desc.ai-md :deep(td) {
|
||||
padding: 4px 8px; border: 0.5px solid var(--df-border); text-align: left;
|
||||
}
|
||||
.detail-desc.ai-md :deep(th) { font-weight: 500; background: var(--df-bg); }
|
||||
|
||||
.detail-section {
|
||||
margin-bottom: var(--df-gap-page);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user