Files
PlantAI-dev/apps/web/vue3/static/interpretation-modal-demo.html
2026-08-03 18:42:47 +08:00

293 lines
16 KiB
HTML
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>阶段性综合深度报告 - 弹窗演示</title>
<script src="https://cdn.jsdelivr.net/npm/vue@3.3.4/dist/vue.global.prod.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/element-plus@2.3.14/dist/index.css">
<script src="https://cdn.jsdelivr.net/npm/element-plus@2.3.14/dist/index.full.js"></script>
<style>
body { margin: 0; padding: 20px; font-family: 'Helvetica Neue', Arial, sans-serif; background: #f5f7fa; }
.demo-container { max-width: 1200px; margin: 0 auto; }
.stage-card { background: white; padding: 15px; margin: 10px 0; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.1); }
.stage-header { display: flex; justify-content: space-between; align-items: center; }
.stage-title { font-size: 16px; font-weight: bold; }
.stage-status { padding: 4px 12px; border-radius: 12px; font-size: 12px; }
.status-completed { background: #67c23a; color: white; }
.status-running { background: #409eff; color: white; }
.status-pending { background: #909399; color: white; }
/* 弹窗样式 */
.interpretation-dialog .el-dialog__body { padding: 0; max-height: 70vh; overflow-y: auto; }
.report-container { padding: 20px; }
.report-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; margin: -20px -20px 20px -20px; }
.report-title { font-size: 20px; font-weight: bold; margin-bottom: 10px; }
.report-meta { font-size: 14px; opacity: 0.9; }
.flow-visualization { display: flex; align-items: center; justify-content: center; padding: 20px; background: #f8f9fa; border-radius: 8px; margin: 20px 0; }
.flow-stage { text-align: center; padding: 10px 20px; }
.flow-stage-icon { font-size: 24px; margin-bottom: 5px; }
.flow-stage-name { font-size: 14px; font-weight: bold; }
.flow-stage-result { font-size: 12px; color: #666; margin-top: 5px; }
.flow-arrow { font-size: 24px; color: #409eff; margin: 0 10px; }
.section-title { font-size: 18px; font-weight: bold; margin: 30px 0 15px 0; padding-bottom: 10px; border-bottom: 2px solid #409eff; }
.connection-card { background: #f0f9ff; border-left: 4px solid #409eff; padding: 15px; margin: 15px 0; border-radius: 4px; }
.connection-header { font-weight: bold; color: #409eff; margin-bottom: 10px; }
.finding-item { background: white; border: 1px solid #e4e7ed; padding: 15px; margin: 10px 0; border-radius: 8px; }
.finding-title { font-weight: bold; color: #303133; margin-bottom: 8px; }
.finding-desc { color: #606266; font-size: 14px; line-height: 1.6; }
.finding-evidence { color: #909399; font-size: 12px; margin-top: 8px; font-style: italic; }
.story-box { background: #fdf6ec; border: 1px solid #f5dab1; padding: 20px; border-radius: 8px; line-height: 1.8; color: #5d4a2a; }
.next-step-item { display: flex; align-items: flex-start; padding: 12px; background: white; border: 1px solid #e4e7ed; margin: 8px 0; border-radius: 6px; }
.next-step-priority { padding: 2px 8px; border-radius: 4px; font-size: 12px; margin-right: 12px; }
.priority-high { background: #fef0f0; color: #f56c6c; }
.priority-medium { background: #fdf6ec; color: #e6a23c; }
.next-step-content { flex: 1; }
.next-step-title { font-weight: bold; margin-bottom: 4px; }
.next-step-desc { font-size: 13px; color: #606266; }
.dialog-footer { display: flex; justify-content: flex-end; gap: 10px; padding: 15px 20px; border-top: 1px solid #e4e7ed; }
</style>
</head>
<body>
<div id="app">
<div class="demo-container">
<h1>🧬 基因家族分析流程</h1>
<p>点击任意阶段的"深度解读"按钮查看阶段性综合报告弹窗</p>
<!-- 分析流程卡片 -->
<div class="stage-card" v-for="stage in stages" :key="stage.id">
<div class="stage-header">
<div>
<span class="stage-title">{{ stage.name }}</span>
<span :class="['stage-status', 'status-' + stage.status]">
{{ statusText[stage.status] }}
</span>
</div>
<el-button
v-if="stage.status === 'completed' || stage.status === 'running'"
type="primary"
size="small"
@click="showInterpretation(stage)"
:loading="loading"
>
🔍 深度解读
</el-button>
</div>
<div v-if="stage.result" style="margin-top: 10px; color: #666; font-size: 14px;">
结果: {{ stage.result }}
</div>
</div>
</div>
<!-- 阶段性综合解读弹窗 -->
<el-dialog
v-model="dialogVisible"
title=" "
width="80%"
:close-on-click-modal="false"
class="interpretation-dialog"
>
<div class="report-container" v-if="report">
<!-- 报告头部 -->
<div class="report-header">
<div class="report-title">🔬 {{ report.metadata.title }}</div>
<div class="report-meta">
分析流程: {{ report.metadata.included_stages.join(' → ') }} |
生成时间: {{ formatTime(report.metadata.generated_at) }}
</div>
</div>
<!-- 流程可视化 -->
<div class="section-title">📈 阶段性进展</div>
<div class="flow-visualization">
<template v-for="(stage, index) in report.stage_flow.stages" :key="stage.id">
<div class="flow-stage">
<div class="flow-stage-icon">{{ index === report.stage_flow.stages.length - 1 ? '🎯' : '✅' }}</div>
<div class="flow-stage-name">{{ stage.name }}</div>
<div class="flow-stage-result">{{ stage.key_result }}</div>
</div>
<div v-if="index < report.stage_flow.stages.length - 1" class="flow-arrow">→</div>
</template>
</div>
<!-- 阶段关联分析 -->
<div class="section-title" v-if="report.stage_connections.length > 0">🔗 阶段间关联分析</div>
<div v-for="(conn, idx) in report.stage_connections" :key="idx" class="connection-card">
<div class="connection-header">{{ conn.from }} → {{ conn.to }}</div>
<div>{{ conn.description }}</div>
<div style="margin-top: 8px; color: #409eff; font-size: 13px;">
💡 {{ conn.insight }}
</div>
</div>
<!-- 关键发现 -->
<div class="section-title">💡 阶段性科学发现</div>
<div v-for="finding in report.key_findings" :key="finding.id" class="finding-item">
<div class="finding-title">{{ finding.id }}. {{ finding.title }}</div>
<div class="finding-desc">{{ finding.description }}</div>
<div class="finding-evidence">📋 证据: {{ finding.evidence }}</div>
</div>
<!-- 生物学故事 -->
<div class="section-title">🧬 生物学故事</div>
<div class="story-box">
{{ report.biological_story }}
</div>
<!-- 下一步建议 -->
<div class="section-title">📋 下一步建议</div>
<div v-for="(step, idx) in report.next_steps" :key="idx" class="next-step-item">
<div :class="['next-step-priority', 'priority-' + step.priority]">
{{ step.priority === 'high' ? '高优先级' : '中优先级' }}
</div>
<div class="next-step-content">
<div class="next-step-title">{{ step.stage }}: {{ step.description }}</div>
<div class="next-step-desc">{{ step.rationale }}</div>
</div>
</div>
</div>
<!-- 弹窗底部按钮 -->
<div class="dialog-footer">
<el-button @click="downloadReport('markdown')">
📄 Markdown
</el-button>
<el-button @click="downloadReport('json')">
📊 JSON
</el-button>
<el-button type="primary" @click="downloadReport('pdf')">
📥 下载PDF报告
</el-button>
<el-button type="success" @click="copyToClipboard">
📋 复制到剪贴板
</el-button>
<el-button @click="dialogVisible = false">
关闭
</el-button>
</div>
</el-dialog>
</div>
<script>
const { createApp } = Vue;
const { ElMessage } = ElementPlus;
createApp({
data() {
return {
loading: false,
dialogVisible: false,
report: null,
stages: [
{ id: 'identify', name: '基因家族鉴定', status: 'completed', result: '24个基因成员' },
{ id: 'phylogeny', name: '系统发育分析', status: 'completed', result: '3个亚家族' },
{ id: 'motif', name: 'Motif分析', status: 'running', result: '分析中...' },
{ id: 'promoter', name: '启动子分析', status: 'pending', result: null },
{ id: 'expression', name: '表达分析', status: 'pending', result: null }
],
statusText: {
completed: '已完成',
running: '进行中',
pending: '待开始'
}
};
},
methods: {
async showInterpretation(stage) {
this.loading = true;
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 800));
// 模拟报告数据
this.report = {
metadata: {
title: `阶段性综合深度报告(identify → phylogeny → ${stage.id})`,
included_stages: ['identify', 'phylogeny', stage.id],
stage_count: 3,
generated_at: new Date().toISOString(),
session_id: 'demo_session_001'
},
stage_flow: {
stages: [
{ id: 'identify', name: '基因家族鉴定', status: 'completed', key_result: '24个基因成员' },
{ id: 'phylogeny', name: '系统发育分析', status: 'completed', key_result: '3个亚家族' },
{ id: stage.id, name: stage.name, status: 'running', key_result: stage.result || '进行中' }
]
},
stage_connections: [
{
from: 'identify',
to: 'phylogeny',
description: '鉴定到的24个基因成员构成了进化树的叶节点,系统发育分析揭示了3个明显的亚家族分化,暗示功能分化可能发生在基因复制事件之后。',
insight: '基因数量与亚家族数量的对应关系支持了基因复制-功能分化的经典进化模式。'
},
{
from: 'phylogeny',
to: stage.id,
description: `基于进化树的亚家族分类,我们在${stage.name}中发现了特异的模式,这与亚家族分化高度一致。`,
insight: '亚家族分化不仅体现在序列水平,也在功能和调控层面有明显差异。'
}
],
key_findings: [
{
id: 1,
title: '基因家族规模适中',
description: '24个成员符合典型植物SPL家族规模(通常15-30个),表明苦荞SPL家族经历了适度的扩张,未出现大规模基因复制事件。',
evidence: '基于HMM(E-value<1e-5)和BLAST(identity>70%)双重验证的鉴定结果'
},
{
id: 2,
title: '明显的亚家族分化',
description: '系统发育分析显示3个明显的亚家族分化,与拟南芥(17成员/3亚家族)、水稻(19成员/3亚家族)等物种的SPL家族结构高度一致。',
evidence: '进化树bootstrap值均>70%,最大似然法构建,支持度高'
},
{
id: 3,
title: '阶段性功能分化证据',
description: `从${stage.name}结果看,不同亚家族表现出特异的模式,支持功能分化的假设。`,
evidence: `${stage.name}结果与亚家族分类高度关联`
}
],
biological_story: `从基因家族鉴定到系统发育分析再到${stage.name},我们构建了一个完整的SPL家族特征图谱。苦荞SPL家族包含24个成员,进化分析显示3个明显的亚家族分化,这可能对应着不同的功能特化。进一步的分析揭示了亚家族特异的模式,为功能研究提供了重要线索。这一阶段性进展为深入理解SPL家族在苦荞生长发育中的作用奠定了基础。`,
next_steps: [
{
priority: 'high',
stage: 'expression',
description: '进行表达分析,验证不同亚家族的时空表达模式',
rationale: '基于当前亚家族分化结果,表达模式验证是功能研究的关键下一步,可以揭示亚家族是否在特定组织或发育阶段特异表达。'
},
{
priority: 'medium',
stage: 'promoter',
description: '启动子分析,探索亚家族特异的调控机制',
rationale: '亚家族分化往往伴随调控元件的差异,启动子分析可以揭示不同亚家族的转录调控特征。'
}
]
};
this.dialogVisible = true;
this.loading = false;
},
formatTime(isoString) {
const date = new Date(isoString);
return date.toLocaleString('zh-CN');
},
downloadReport(format) {
ElMessage.success(`开始下载${format.toUpperCase()}格式报告...`);
// 实际实现:调用 /api/agent/report/{session_id}/download?format={format}
},
copyToClipboard() {
const text = this.report.biological_story;
navigator.clipboard.writeText(text).then(() => {
ElMessage.success('已复制到剪贴板');
});
}
}
}).use(ElementPlus).mount('#app');
</script>
</body>
</html>