@@ -1,10 +1,10 @@
< template >
< div class = "ideas" >
< header class = "page-header" >
< h1 > { { $t ( 'ideas.title' ) } } < / h1 >
< h1 > 💡 灵感 < / h1 >
< div class = "header-actions" >
< button class = "btn btn-ghost" @click ="refresh" > {{ $ t ( ' ideas.refresh ' ) }} < / button >
< button class = "btn btn-primary" @click ="openCaptureModal" > {{ $ t ( ' ideas.capture ' ) }} < / button >
< button class = "btn btn-ghost" @click ="refresh" > 🔄 刷新 < / button >
< button class = "btn btn-primary" @click ="openCaptureModal" > ✨ 捕捉灵感 < / button >
< / div >
< / header >
@@ -14,7 +14,8 @@
< input
v-model = "searchQuery"
type = "text"
: placeholder= "$t('ideas.searchPlaceholder') "
placeholder= "搜索灵感... "
@input ="filterIdeas"
/ >
< / div >
< button
@@ -24,13 +25,13 @@
: class = "{ active: activeFilter === f.key }"
@click ="activeFilter = f.key"
>
{ { f . icon } } { { $t ( f. labelKey ) } }
{ { f . icon } } { { f . label } }
< / button >
< / div >
<!-- 两栏布局 -- >
< div class = "ideas-layout" >
<!-- 左侧 : 灵感 列表 -- >
<!-- 左侧 : 想法 列表 -- >
< section class = "idea-list-panel" >
< div class = "idea-list" >
< div
@@ -46,39 +47,31 @@
< / div >
< p class = "idea-desc-preview" > { { idea . description ? . slice ( 0 , 60 ) ? ? '' } } { { idea . description && idea . description . length > 60 ? '...' : '' } } < / p >
< div class = "idea-card-footer" >
< span class = "status-tag" : class = "'status-' + idea.status" > { { $t ( statusLabelKey ( idea . status ) ) } } < / span >
< span class = "status-tag" : class = "'status-' + idea.status" > { { statusLabel ( idea . status ) } } < / span >
< span class = "idea-date" > { { formatDate ( idea . created _at ) } } < / span >
< / div >
< / div >
< / div >
< / section >
<!-- 右侧 : 灵感 详情 -- >
<!-- 右侧 : 想法 详情 -- >
< section class = "idea-detail-panel" v-if = "currentIdea" >
< div class = "detail-header" >
< h2 class = "detail-title" > { { currentIdea . title } } < / h2 >
< span class = "status-tag" : class = "'status-' + currentIdea.status" > { { $t ( statusLabelKey ( currentIdea . status ) ) } } < / span >
< span class = "status-tag" : class = "'status-' + currentIdea.status" > { { statusLabel ( currentIdea . status ) } } < / span >
< / div >
<!-- 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 >
< p class = "detail-desc" > { { currentIdea . description } } < / p >
<!-- 对抗式评估 -- >
< div class = "detail-section" >
< h3 > { { $t ( 'ideas.adversarialTitle' ) } } < span class = "eval-mode-tag" > { { $t ( 'ideas.evalModeHeuristic' ) } } < / span > < / h3 >
< h3 > ⚖ ️ 对抗式评估 < / h3 >
< div v-if = "adversarialEval" class="adversarial-eval" >
< ! - - 正反方观点 - - >
< div class = "debate-container" >
< div class = "debate-column positive" >
< h4 > { { $t ( 'ideas.positive' ) } } < / h4 >
< div class = "confidence-bar" >
< div class = "confidence-fill" : style = "{ width: ( adversarialEval. positive_strength * 100) + '%' }" > < / div >
< / div >
< div class = "confidence-text" > { { $t ( 'ideas.confidence' , { n : ( adversarialEval . positive _strength * 100 ) . toFixed ( 0 ) } ) } } < / div >
< h4 > 📈 正方观点 < / h4 >
< div class = "confidence-bar" : style = "{ width: (adversarialEval.positive_strength * 100) + '%' }" > < / div >
< div class = "confidence-text" > { { ( adversarialEval. positive _strength * 100 ) . toFixed ( 0 ) } } % 置信度 < / div >
< p class = "thesis" > { { adversarialEval . positive . thesis } } < / p >
< ul >
< li v-for = "evidence in adversarialEval.positive.evidence" :key="evidence" > • {{ evidence }} < / li >
@@ -86,11 +79,9 @@
< / div >
< div class = "debate-column negative" >
< h4 > { { $t ( 'ideas.negative' ) } } < / h4 >
< div class = "confidence-bar" >
< div class = "confidence-fill" : style = "{ width: ( adversarialEval. negative_strength * 100) + '%' }" > < / div >
< / div >
< div class = "confidence-text" > { { $t ( 'ideas.confidence' , { n : ( adversarialEval . negative _strength * 100 ) . toFixed ( 0 ) } ) } } < / div >
< h4 > 📉 反方观点 < / h4 >
< div class = "confidence-bar" : style = "{ width: (adversarialEval.negative_strength * 100) + '%' }" > < / div >
< div class = "confidence-text" > { { ( adversarialEval. negative _strength * 100 ) . toFixed ( 0 ) } } % 置信度 < / div >
< p class = "thesis" > { { adversarialEval . negative . thesis } } < / p >
< ul >
< li v-for = "evidence in adversarialEval.negative.evidence" :key="evidence" > • {{ evidence }} < / li >
@@ -100,36 +91,34 @@
< ! - - AI 分析师结论 - - >
< div class = "analyst-conclusion" >
< h4 > { { $t ( 'ideas.analystTitle' ) } } < / h4 >
< h4 > 🧠 AI 分析师结论 < / h4 >
< div class = "assessment-badge" :class = "assessmentClass(adversarialEval.recommendation)" >
{ { assessmentLabel ( adversarialEval . recommendation ) } }
< / div >
< p class = "final-score" > { { $t ( 'ideas.finalScore' , { score : adversarialEval . final _score . toFixed ( 1 ) } ) } } < / p >
< p class = "final-score" > 综合评分 : { { adversarialEval . final _score . toFixed ( 1 ) } } / 10 < / p >
< div class = "net-sentiment" :class = "sentimentClass(adversarialEval.net_sentiment)" >
{ { $t ( 'ideas.netSentiment' , { tone : adversarialEval . net _sentiment > 0 ? $t ( 'ideas.sentimentPositive' ) : ( adversarialEval . net _sentiment < 0 ? $t ( 'ideas.sentimentNegative' ) : $t ( 'ideas.sentimentNeutral' ) ) , n : ( adversarialEval . net _sentiment * 100 ) . toFixed ( 0 ) } ) } }
整体倾向 : { { adversarialEval . net _sentiment > 0 ? '积极' : '谨慎' } }
( { { ( adversarialEval . net _sentiment * 100 ) . toFixed ( 0 ) } } )
< / div >
< p class = "summary" > { { adversarialEval . analyst . summary } } < / p >
< / div >
<!-- 行动建议 -- >
< div class = "action-recommendations" >
< h4 > { { $t ( 'ideas.actionTitle' ) } } < / h4 >
< h4 > 💡 行动建议 < / h4 >
< ul >
< li v-for = "action in adversarialEval.action_items" :key="action" > • {{ action }} < / li >
< / ul >
< / div >
< / div >
< div v-else class = "eval-report" style = "opacity:0.5" >
< button class = "btn-evaluate" :disabled = "evaluating" @click ="evaluateCurrentIdea" >
{{ evaluating ? $ t ( ' ideas.evaluating ' ) : $ t ( ' ideas.startEval ' ) }}
< / button >
< div v-if = "evalError" class="eval-error" > ⚠ ️ {{ evalError }} < / div >
< button class = "btn-evaluate" @click ="evaluateCurrentIdea" > 🔍 开始对抗评估 < / button >
< / div >
< / div >
< ! - - 传统评分雷达图 - - >
< div class = "detail-section" >
< h3 > { { $t ( 'ideas.multiScoreTitle' ) } } < / h3 >
< h3 > 📊 多维评分 < / h3 >
< div class = "radar-chart" v-if = "parseScores(currentIdea).length > 0" >
< div class = "radar-row" v-for = "dim in parseScores(currentIdea)" :key="dim.name" >
< span class = "radar-label" > { { dim . name } } < / span >
@@ -139,24 +128,28 @@
< span class = "radar-value" > { { dim . score } } < / span >
< / div >
< / div >
< div v-else class = "eval-report" style = "opacity:0.5" > { { $t ( 'ideas.noEval' ) } } < / div >
< div v-else class = "eval-report" style = "opacity:0.5" > 暂无评估 < / div >
< / div >
<!-- 标签 -- >
< div class = "detail-section" >
< h3 > { { $t ( 'ideas.tagsTitle' ) } } < / h3 >
< h3 > 🏷 ️ 标签 < / h3 >
< div class = "tag-list" v-if = "parseTags(currentIdea).length > 0" >
< span class = "tag" v-for = "tag in parseTags(currentIdea)" :key="tag" > {{ tag }} < / span >
< / div >
< div v-else class = "eval-report" style = "opacity:0.5" > { { $t ( 'ideas.noTags' ) } } < / div >
< div v-else class = "eval-report" style = "opacity:0.5" > 暂无标签 < / div >
< / div >
<!-- 状态管理 -- >
< div class = "detail-section" >
< h3 > { { $t ( 'ideas.statusTitle' ) } } < / h3 >
< h3 > 📋 状态管理 < / h3 >
< div class = "status-controls" >
< select :value = "currentStatus" @change="onStatusChange" class = "status-select" >
< option v-for = "s in statusOptions" :key="s.value" :value="s.value "> {{ $ t ( s.labelKey ) }} < / option >
< select v-model = "currentStatus" @change="updateIdeaStatus" class= "status-select" >
< option value = "draft "> 📝 草稿 < / option >
< option value = "pending_review" > ⏳ 待评估 < / option >
< option value = "approved" > ✅ 已批准 < / option >
< option value = "promoted" > 🚀 已立项 < / option >
< option value = "rejected" > ❌ 已拒绝 < / option >
< / select >
< / div >
< / div >
@@ -169,9 +162,9 @@
class = "btn btn-primary"
@click ="promoteToProject"
>
{ { $t ( 'ideas.promoteToProject' ) } }
🚀 立项为项目
< / button >
< button class = "btn btn-ghost" @click ="deleteCurrentIdea" > {{ $ t ( ' ideas.deleteIdea ' ) }} < / button >
< button class = "btn btn-ghost" @click ="deleteCurrentIdea" > 🗑 ️ 删除想法 < / button >
< / div >
< / div >
< / section >
@@ -180,82 +173,57 @@
< section class = "idea-detail-panel idea-empty" v-else >
< div class = "empty-state" >
< div class = "empty-icon" > 💡 < / div >
< p > { { $t ( 'ideas.emptyState' ) } } < / p >
< p > 选择一个想法查看详情 < / p >
< / div >
< / section >
< / div >
<!-- 捕捉灵感 模态框 -- >
<!-- 捕捉想法 模态框 -- >
< div class = "modal-overlay" v-if = "showCaptureModal" @click.self="showCaptureModal = false" >
< div class = "modal-box" >
< h3 > { { $t ( 'ideas.captureTitle' ) } } < / h3 >
< label style = "font-size:12px;color:var(--df-text-secondary);margin-bottom:4px;display:block" > { { $t ( 'ideas.fieldTitle' ) } } < / label >
< input v-model = "newIdeaTitle" : placeholder="$t('ideas.titlePlaceholder') " @keyup.enter="confirmCapture" / >
< label style = "font-size:12px;color:var(--df-text-secondary);margin-bottom:4px;display:block" > { { $t ( 'ideas.fieldDesc' ) } } < / label >
< textarea v-model = "newIdeaDesc" : placeholder="$t('ideas.descPlaceholder') " rows="3" style="resize:vertical" > < / textarea >
< h3 > ✨ 捕捉新想法 < / h3 >
< label style = "font-size:12px;color:var(--df-text-secondary);margin-bottom:4px;display:block" > 标题 < / label >
< input v-model = "newIdeaTitle" placeholder="一句话描述你的想法... " @keyup.enter="confirmCapture" / >
< label style = "font-size:12px;color:var(--df-text-secondary);margin-bottom:4px;display:block" > 描述 < / label >
< textarea v-model = "newIdeaDesc" placeholder="详细说明(可选)... " rows="3" style="resize:vertical" > < / textarea >
< div class = "modal-actions" >
< button class = "btn-cancel" @click ="showCaptureModal = false" > {{ $ t ( ' common.cancel ' ) }} < / button >
< button class = "btn-confirm" @click ="confirmCapture" > {{ $ t ( ' common.confirm ' ) }} < / button >
< button class = "btn-cancel" @click ="showCaptureModal = false" > 取消 < / button >
< button class = "btn-confirm" @click ="confirmCapture" > 确认 < / button >
< / div >
< / div >
< / div >
< ! - - 确认弹层 ( 删除灵感 , 替代原生 window.confirm ) - - >
< ConfirmDialog :visible = "confirmState.visible" :msg = "confirmState.msg" @result ="answerConfirm" / >
< / div >
< / template >
< script setup lang = "ts" >
import { ref , computed , onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { Message } from '@arco-design/web-vue'
import { ref , computed , onMounted , watch } from 'vue'
import { useRouter , useRoute } from 'vue-router'
import { useProjectStore } from '../stores/project'
import { formatDate } from '../utils/time'
import ConfirmDialog from '../components/ConfirmDialog.vue'
import { useConfirm } from '../composables/useConfirm'
import { useRendered } from '../composables/useMarkdown'
import type { IdeaRecord } from '../api/types'
const { t } = useI18n ( )
const router = useRouter ( )
const route = useRoute ( )
const store = useProjectStore ( )
// 确认弹层状态机抽至 composables/useConfirm(原 4 视图重复:Projects/ProjectDetail/Ideas/Settings)
const { confirmState , confirmDialog , answerConfirm } = useConfirm ( )
type FilterKey = 'all' | 'hot' | 'pending' | 'promoted'
const activeFilter = ref < FilterKey > ( 'all' )
const selectedId = ref < string | null > ( null )
const searchQuery = ref ( '' )
// ── 新建灵感 模态框 ──
// ── 新建想法 模态框 ──
const showCaptureModal = ref ( false )
const newIdeaTitle = ref ( '' )
const newIdeaDesc = ref ( '' )
const filters : { key : FilterKey ; labelKey : string ; icon : string } [ ] = [
{ key : 'all' , labelKey : 'ideas.filter.all ' , icon : '📋' } ,
{ key : 'hot' , labelKey : 'ideas.filter.hot ' , icon : '🔥' } ,
{ key : 'pending' , labelKey : 'ideas.filter.pending ' , icon : '⏳' } ,
{ key : 'promoted' , labelKey : 'ideas.filter.promoted ' , icon : '🚀' } ,
const filters : { key : FilterKey ; label : string ; icon : string } [ ] = [
{ key : 'all' , label : '全部 ' , icon : '📋' } ,
{ key : 'hot' , label : '热门 ' , icon : '🔥' } ,
{ key : 'pending' , label : '待评估 ' , icon : '⏳' } ,
{ key : 'promoted' , label : '已立项 ' , icon : '🚀' } ,
]
// ── 状态映射(单一数据源,列表/详情/下拉共用) ──
const statusOptions : { value : string ; labelKey : string } [ ] = [
{ value : 'draft' , labelKey : 'ideas.status.draft' } ,
{ value : 'pending_review' , labelKey : 'ideas.status.pending_review' } ,
{ value : 'approved' , labelKey : 'ideas.status.approved' } ,
{ value : 'promoted' , labelKey : 'ideas.status.promoted' } ,
{ value : 'rejected' , labelKey : 'ideas.status.rejected' } ,
]
// 任意 status 字符串 → 对应 i18n key;未知状态回退到原值显示
function statusLabelKey ( status : string ) : string {
return statusOptions . find ( s => s . value === status ) ? . labelKey ? ? status
}
const filteredIdeas = computed ( ( ) => {
let ideas = store . ideas
@@ -280,17 +248,15 @@ const filteredIdeas = computed(() => {
return ideas
} )
function filterIdeas ( ) {
// filteredIdeas 是 computed, 会自动响应变化
}
const currentIdea = computed ( ( ) => {
if ( ! selectedId . value ) return null
return store . ideas . find ( i => i . id === selectedId . value ) ? ? null
} )
// B-260615-25:灵感描述 Markdown 渲染(复用 AiChat/TaskDetail 同款渲染器,模块级单例),
// useRendered 封装 computed(读 mdReady 触发响应式 + renderMd)+ ensureLoaded(幂等预热)
const { rendered : renderedDesc , ensureLoaded } = useRendered (
( ) => currentIdea . value ? . description ? ? '' ,
)
const currentStatus = computed ( ( ) => {
return currentIdea . value ? . status || 'draft'
} )
@@ -348,30 +314,23 @@ const adversarialEval = computed<AdversarialEval | null>(() => {
} )
function assessmentClass ( recommendation : string ) {
// 映射到 CSS 定义的 badge 颜色类(.immediate/.soon/.conditional/.revised/.defer/.cancel)
const map : Record < string , string > = {
'immediate action' : 'immediate' ,
'soon' : 'soon' ,
'with resources' : 'conditional' ,
'research more' : 'revised' ,
'monitor' : 'defer' ,
'cancel' : 'cancel' ,
}
return map [ recommendation . toLowerCase ( ) ] ? ? 'conditional'
return recommendation . toLowerCase ( ) . replace ( / /g , '-' )
}
function assessmentLabel ( recommendation : string ) {
const key = ` ideas.assessment. ${ recommendation } `
// 未命中 i18n key 时回退到原始 recommendation 字符串
const translated = t ( key )
return translated === key ? recommendation : translated
const map : Record < string , string > = {
'immediate action' : '🚀 立即行动' ,
'soon' : '📅 尽快行动' ,
'with resources' : '📦 配置资源后行动' ,
'research more' : '🔍 需要更多研究' ,
'monitor' : '👁️ 持续监控' ,
'cancel' : '❌ 取消想法'
}
return map [ recommendation ] ? ? recommendation
}
function sentimentClass ( sentiment : number ) {
// 统一三档(FR-C2: 原 >=0 与模板 >0 矛盾, net_sentiment=0 时文案负面样式 positive)
if ( sentiment > 0 ) return 'positive'
if ( sentiment < 0 ) return 'negative'
return 'neutral'
return sentiment >= 0 ? 'positive' : 'negative'
}
function scoreClass ( score : number | null ) {
@@ -381,6 +340,17 @@ function scoreClass(score: number | null) {
return 'score-low'
}
function statusLabel ( status : string ) {
const map : Record < string , string > = {
draft : '📝 草稿' ,
pending _review : '⏳ 待评估' ,
approved : '✅ 已批准' ,
promoted : '🚀 已立项' ,
rejected : '❌ 已拒绝' ,
}
return map [ status ] ? ? status
}
// formatDate 由 ../utils/time 提供(统一毫秒字符串解析,根治 Invalid Date)
function openCaptureModal ( ) {
@@ -400,54 +370,104 @@ async function confirmCapture() {
async function deleteCurrentIdea ( ) {
if ( ! currentIdea . value ) return
if ( ! await confirmDialog ( t ( 'ideas.confirmDelete' , { title : currentIdea . value . title } ) ) ) return
await store . deleteIdea ( currentIdea . value . id )
selectedId . value = null
}
async function promoteToProject ( ) {
if ( ! currentIdea . value ) return
try {
const res = await store . promoteIdea ( currentIdea . value . id )
router . push ( ` /projects/ ${ res . project _id } ` )
} catch ( e : any ) {
const msg = e ? . toString ( ) ? ? t ( 'ideas.promoteFailed' )
console . error ( t ( 'ideas.promoteFailed' ) , e )
Message . error ( msg )
}
// 创建新项目, 基于想法( store.createProject 第 3 参 = idea_id)
const project = await sto re. createProject (
currentIdea . value . title ,
currentIdea . value . description ,
currentIdea . value . id ,
)
if ( ! project ) return
const projectId = project . id
// 更新想法状态和晋升信息( store.updateIdea 单字段,分两次调用)
await store . updateIdea ( currentIdea . value . id , 'status' , 'promoted' )
await store . updateIdea ( currentIdea . value . id , 'promoted_to' , projectId )
// 跳转到项目详情
router . push ( ` /projects/ ${ projectId } ` )
// 刷新想法列表
await store . loadIdeas ( )
}
async function refresh ( ) {
await store . loadIdeas ( )
}
const evaluating = ref ( false )
const evalError = ref ( '' )
async function evaluateCurrentIdea ( ) {
if ( ! currentIdea . value || evaluating . value ) return
if ( ! currentIdea . value ) return
evaluating . value = true
evalError . value = ''
try {
await store . evaluateIdea ( currentIdea . value . id )
} catch ( e : any ) {
evalError . value = e ? . toString ( ) ? ? t ( 'ideas.evalFailed' )
console . error ( t ( 'ideas.evalFailed' ) , e )
} finally {
evaluating . value = false
// 模拟对抗式评估(实际应该调用后端 API)
// 这里使用模拟数据展示界面
const mockEval : AdversarialEval = {
positive _strength : 0.75 ,
negative _strength : 0.65 ,
net _sentiment : 0.1 ,
recommendation : 'with resources' ,
final _score : 6.5 ,
summary : '该想法整体价值评估中等偏上,建议在有条件的情况下执行。主要价值在于技术创新性较强,需要关注风险控制和资源投入。' ,
action _items : [
'确认资源预算' ,
'评估ROI' ,
'制定风险预案'
] ,
positive : {
thesis : '技术创新性强,潜在回报高' ,
evidence : [ '技术栈成熟' , '市场需求明确' , '团队有相关经验' ] ,
} ,
negative : {
thesis : '资源投入大,存在执行风险' ,
evidence : [ '开发周期长' , '需要额外人力' , '竞品已有类似方案' ] ,
} ,
analyst : {
summary : '综合正反方观点,建议在资源到位后启动,并设立阶段性验收点控制风险。' ,
} ,
}
// 更新想法的评估结果(实际应该调用 API)
await store . updateIdea ( currentIdea . value . id , 'ai_analysis' , JSON . stringify ( mockEval , null , 2 ) )
}
async function onStatusChange ( e : Event ) {
if ( ! currentIdea . value ) return
const newStatus = ( e . target as HTMLSelectElement ) . value
await store . updateIdea ( currentIdea . value . id , 'status' , newStatus )
async function updateIdeaStatus ( ) {
if ( ! currentIdea . value || ! currentStatus . value ) return
await store . updateIdea ( currentIdea . value . id , 'status' , currentStatus . value )
}
onMounted ( async ( ) => {
ensureLoaded ( ) // 后台预热 Markdown 渲染器(模块单例,与 AiChat/TaskDetail 共享),不阻塞
await store . loadIdeas ( )
// 支持从 /ideas/:id 路由直接打开指定灵感
const id = route . params . id as string | undefined
if ( id ) {
const exists = store . ideas . some ( i => i . id === id )
if ( exists ) {
selectedId . value = id
} else {
console . warn ( ` [Ideas] 路由指定的灵感 id= ${ id } 不在当前列表中 ` )
}
}
} )
// 支持在同组件内切换 /ideas/:id( 如从灵感来源链接跳转)
watch ( ( ) => route . params . id , ( newId ) => {
const id = newId as string | undefined
if ( id ) {
const exists = store . ideas . some ( i => i . id === id )
if ( exists ) {
selectedId . value = id
} else {
console . warn ( ` [Ideas] 路由切换的灵感 id= ${ id } 不在当前列表中 ` )
}
} else {
// 回到 /ideas( 无 id) 时不清空, 保留用户选择
}
} )
< / script >
@@ -636,14 +656,25 @@ onMounted(async () => {
overflow : hidden ;
}
. confidence - fill {
. debate - column . positive . confidence - bar : : after {
content : '' ;
position : absolute ;
left : 0 ;
top : 0 ;
height : 100 % ;
background : currentColor ;
border - radius : var ( -- df - radius - xs ) ;
transition : width 0.4 s ;
}
. debate - column . posi tive . confidence - fill { background : var ( -- df - success ) ; }
. debate - column . negative . confidence - fill { background : var ( -- df - danger ) ; }
. debate - column . nega tive . confidence - bar : : after {
content : '' ;
position : absolute ;
left : 0 ;
top : 0 ;
height : 100 % ;
background : currentColor ;
border - radius : var ( -- df - radius - xs ) ;
}
. confidence - text {
font - size : 11 px ;
@@ -761,23 +792,6 @@ onMounted(async () => {
background : var ( -- df - accent - hover ) ;
}
. eval - error {
margin - top : 8 px ;
font - size : 12 px ;
color : var ( -- df - danger ) ;
}
. eval - mode - tag {
font - size : 11 px ;
font - weight : 400 ;
padding : 1 px 8 px ;
border - radius : var ( -- df - radius - xs ) ;
background : rgba ( 255 , 217 , 61 , 0.15 ) ;
color : var ( -- df - warning ) ;
margin - left : 6 px ;
vertical - align : middle ;
}
/* ===== 右侧详情 ===== */
. idea - detail - panel {
background : var ( -- df - bg - card ) ;
@@ -811,63 +825,6 @@ 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 6 px ; }
. detail - desc . ai - md : deep ( p : last - child ) { margin - bottom : 0 ; }
. detail - desc . ai - md : deep ( ul ) , . detail - desc . ai - md : deep ( ol ) { margin : 4 px 0 ; padding - left : 20 px ; }
. detail - desc . ai - md : deep ( li ) { margin : 2 px 0 ; line - height : 1.5 ; }
. detail - desc . ai - md : deep ( code ) {
font - family : var ( -- df - font - mono ) ;
font - size : 12 px ;
padding : 1 px 5 px ;
background : rgba ( 255 , 255 , 255 , 0.06 ) ;
border - radius : var ( -- df - radius - sm ) ;
color : var ( -- df - accent ) ;
}
. detail - desc . ai - md : deep ( pre ) {
margin : 8 px 0 ;
padding : 10 px 12 px ;
background : var ( -- df - bg ) ;
border : 0.5 px 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 : 12 px ;
line - height : 1.5 ;
}
. detail - desc . ai - md : deep ( blockquote ) {
margin : 6 px 0 ;
padding : 4 px 12 px ;
border - left : 2 px 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 : 8 px 0 4 px ;
}
. detail - desc . ai - md : deep ( h1 ) { font - size : 16 px ; }
. detail - desc . ai - md : deep ( h2 ) { font - size : 14 px ; }
. detail - desc . ai - md : deep ( h3 ) { font - size : 13 px ; }
. 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.5 px solid var ( -- df - border ) ; margin : 8 px 0 ; }
. detail - desc . ai - md : deep ( table ) {
width : 100 % ; border - collapse : collapse ; margin : 6 px 0 ;
font - size : 12 px ; overflow - x : auto ; display : block ;
}
. detail - desc . ai - md : deep ( th ) , . detail - desc . ai - md : deep ( td ) {
padding : 4 px 8 px ; border : 0.5 px 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 ) ;
}