diff --git a/.gitignore b/.gitignore
index d5e2ea7f0..eedcfcbf5 100755
--- a/.gitignore
+++ b/.gitignore
@@ -18,7 +18,7 @@ outputs/
input/
workspace/
paper/
-memory/
+#memory/
memory_storage/
backup/
backups/
@@ -29,3 +29,4 @@ nohup.out
Rplots.pdf
*.pyc
*.log
+.gitignore
diff --git a/apps/cli/run_gene_family_report.py b/apps/cli/run_gene_family_report.py
new file mode 100644
index 000000000..a2edf8ab2
--- /dev/null
+++ b/apps/cli/run_gene_family_report.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+"""CLI for the plan-scoped formal gene-family result report."""
+
+from __future__ import annotations
+
+import argparse
+import json
+from pathlib import Path
+import sys
+from typing import Sequence
+
+PROJECT_ROOT = Path(__file__).resolve().parents[2]
+if str(PROJECT_ROOT) not in sys.path:
+ sys.path.insert(0, str(PROJECT_ROOT))
+
+from gfa_core.gene_family_report import GeneFamilyReportResult, generate_gene_family_report
+
+
+def _parser() -> argparse.ArgumentParser:
+ parser = argparse.ArgumentParser(description="Generate or open a gene-family result report")
+ parser.add_argument("--workspace", type=Path, required=True)
+ parser.add_argument("--prefix", required=True)
+ parser.add_argument("--job-id", default="")
+ parser.add_argument("--expected-stages", default="")
+ parser.add_argument("--stage-statuses-json", default="{}")
+ parser.add_argument("--scope-id", default="")
+ parser.add_argument("--analysis-route", default="")
+ parser.add_argument("--member-set-id", default="")
+ parser.add_argument("--rnaseq-scope-id", default="")
+ parser.add_argument("--action", choices=("generate", "open"), default="generate")
+ parser.add_argument("--no-model", action="store_true")
+ return parser
+
+
+def main(argv: Sequence[str] | None = None) -> GeneFamilyReportResult:
+ args = _parser().parse_args(argv)
+ statuses = json.loads(args.stage_statuses_json)
+ if not isinstance(statuses, dict):
+ raise ValueError("--stage-statuses-json must contain a JSON object")
+ result = generate_gene_family_report(
+ args.workspace,
+ args.prefix,
+ expected_stages=[value.strip() for value in args.expected_stages.split(",") if value.strip()],
+ stage_statuses={str(key): str(value) for key, value in statuses.items()},
+ scope_id=args.scope_id,
+ analysis_route=args.analysis_route,
+ member_set_id=args.member_set_id,
+ rnaseq_scope_id=args.rnaseq_scope_id,
+ use_model=not args.no_model,
+ action=args.action,
+ job_id=args.job_id,
+ )
+ print(
+ json.dumps(
+ {
+ "status": "success",
+ "report_path": str(result.report_path),
+ "manifest_path": str(result.manifest_path),
+ "report_status": result.report_status,
+ "generation_mode": result.generation_mode,
+ "reused": result.reused,
+ },
+ ensure_ascii=False,
+ )
+ )
+ return result
+
+
+if __name__ == "__main__":
+ main()
diff --git a/apps/web/pipeline_backend.py b/apps/web/pipeline_backend.py
index 448953c59..38a17a464 100644
--- a/apps/web/pipeline_backend.py
+++ b/apps/web/pipeline_backend.py
@@ -6112,8 +6112,9 @@ def run_stage(
target_species = kwargs.get("target_species")
interspecies_ref_species = kwargs.get("interspecies_ref_species")
- scope_file = str(kwargs.get("scope_file") or kwargs.get("scopeFile") or "").strip()
- analysis_route = str(kwargs.get("analysis_route") or kwargs.get("analysisRoute") or "").strip()
+ scope_file = str(kwargs.get("scope_file") or kwargs.get("scopeFile") or "").strip()
+ scope_id = str(kwargs.get("scope_id") or kwargs.get("scopeId") or "").strip()
+ analysis_route = str(kwargs.get("analysis_route") or kwargs.get("analysisRoute") or "").strip()
analysis_mode = str(kwargs.get("analysis_mode") or kwargs.get("analysisMode") or "").strip()
member_set_id = str(kwargs.get("member_set_id") or kwargs.get("memberSetId") or "").strip()
combo_output_label = str(combo_output_label or kwargs.get("comboOutputLabel") or kwargs.get("combo_output_label") or "").strip()
@@ -8432,7 +8433,62 @@ def run_stage(
else:
return f"❌ PEP failed (exit code: {code}). Log: {log_file}", manifest
- if stage == "summary":
+ if stage == "gene_family_report":
+ expected_stages = kwargs.get("expected_stages") or kwargs.get("expectedStages") or []
+ if isinstance(expected_stages, str):
+ expected_stages = [value.strip() for value in expected_stages.split(",") if value.strip()]
+ else:
+ expected_stages = [str(value).strip() for value in expected_stages if str(value).strip()]
+ report_action = str(kwargs.get("report_action") or kwargs.get("reportAction") or "generate").strip()
+ if report_action == "generate" and not expected_stages:
+ return "❌ 基因家族结果报告生成需要非空 expected_stages", manifest
+ stage_statuses = kwargs.get("stage_statuses") or kwargs.get("stageStatuses") or {}
+ if isinstance(stage_statuses, str):
+ try:
+ stage_statuses = json.loads(stage_statuses)
+ except json.JSONDecodeError:
+ return "❌ 基因家族结果报告的 stage_statuses 不是合法 JSON", manifest
+ if not isinstance(stage_statuses, dict):
+ return "❌ 基因家族结果报告的 stage_statuses 必须是对象", manifest
+ report_use_model_raw = kwargs.get("report_use_model", kwargs.get("reportUseModel", True))
+ rnaseq_scope_id = str(kwargs.get("rnaseq_scope_id") or kwargs.get("rnaseqScopeId") or "").strip()
+ cmd = [
+ sys.executable,
+ "apps/cli/run_gene_family_report.py",
+ "--workspace",
+ str(ws),
+ "--prefix",
+ prefix,
+ "--job-id",
+ job.job_id,
+ "--expected-stages",
+ ",".join(expected_stages),
+ "--stage-statuses-json",
+ json.dumps(stage_statuses, ensure_ascii=False, separators=(",", ":")),
+ "--action",
+ report_action,
+ ]
+ for flag, value in (
+ ("--scope-id", scope_id),
+ ("--analysis-route", analysis_route),
+ ("--member-set-id", member_set_id),
+ ("--rnaseq-scope-id", rnaseq_scope_id),
+ ):
+ if value:
+ cmd += [flag, value]
+ if not _truthy(report_use_model_raw):
+ cmd.append("--no-model")
+ code = run_cmd_logged(cmd, log_file, cwd=Path("."))
+ report_manifests = sorted(
+ final_dir.glob(f"{prefix}*.gene_family_report.manifest.json"),
+ key=lambda path: path.stat().st_mtime,
+ )
+ report_manifest = report_manifests[-1] if report_manifests else manifest
+ if code != 0:
+ return f"❌ 基因家族结果报告生成失败,日志:{log_file}", report_manifest
+ return f"✅ 基因家族结果报告已生成。日志:{log_file}", report_manifest
+
+ if stage == "summary":
summary_manifest_seed = (
ws / "final" / f"{prefix}.identify.manifest.json"
if (ws / "final" / f"{prefix}.identify.manifest.json").exists()
diff --git a/apps/web/vue3/agent_status_component_ml.html b/apps/web/vue3/agent_status_component_ml.html
new file mode 100755
index 000000000..dead607b1
--- /dev/null
+++ b/apps/web/vue3/agent_status_component_ml.html
@@ -0,0 +1,416 @@
+
+
+
+
+
+
+
+
{{ getRoleLabel(msg.role) }}
+
+
{{ formatTime(msg.timestamp) }}
+
+
+
+
+
+
+ {{ isProcessing ? t('processing') : t('send') }}
+
+
+
+
+
{{ t('task-list') }}
+
+ {{ getStatusIcon(task.status) }}
+ {{ task.message }}
+ {{ task.progress }}%
+
+
+
+
+
+
+// Agent状态组件
+const AgentStatusComponent = {
+ template: '#agent-status-component',
+
+ data() {
+ return {
+ messages: [],
+ userInput: '',
+ isProcessing: false,
+ currentMode: '',
+ sessionId: '',
+ tasks: [],
+ currentTask: null,
+ pollingInterval: null
+ };
+ },
+
+ mounted() {
+ this.sessionId = 'session_' + Date.now();
+ this.currentMode = t('standby');
+ this.loadTasks();
+ this.startPolling();
+ },
+
+ beforeUnmount() {
+ this.stopPolling();
+ },
+
+ methods: {
+ async sendMessage() {
+ if (!this.userInput.trim() || this.isProcessing) return;
+
+ const message = this.userInput.trim();
+ this.userInput = '';
+ this.isProcessing = true;
+
+ // 添加用户消息
+ this.addMessage('user', message);
+
+ try {
+ // 调用Agent执行API
+ const response = await fetch('/api/agent/execute', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ message: message,
+ session_id: this.sessionId,
+ model: 'kimi'
+ })
+ });
+
+ const data = await response.json();
+
+ if (data.success) {
+ this.currentTask = data.task_id;
+ this.addMessage('system', t('msg-task-submitted') + data.task_id);
+ this.loadTasks();
+ } else {
+ this.addMessage('error', data.message || t('msg-submit-failed'));
+ }
+ } catch (error) {
+ this.addMessage('error', t('msg-request-failed') + error.message);
+ }
+
+ this.isProcessing = false;
+ },
+
+ async loadTasks() {
+ try {
+ const response = await fetch('/api/agent/tasks');
+ const data = await response.json();
+
+ if (data.success) {
+ this.tasks = data.tasks;
+ }
+ } catch (error) {
+ console.error(t('msg-load-task-failed'), error);
+ }
+ },
+
+ async checkTaskStatus(taskId) {
+ try {
+ const response = await fetch(`/api/agent/status/${taskId}`);
+ const data = await response.json();
+
+ if (data.success && data.task) {
+ const task = data.task;
+
+ // 更新任务状态
+ const taskIndex = this.tasks.findIndex(t => t.task_id === taskId);
+ if (taskIndex >= 0) {
+ this.tasks[taskIndex] = { ...this.tasks[taskIndex], ...task };
+ }
+
+ // 如果任务完成,显示结果
+ if (task.status === 'completed' && task.result) {
+ this.addMessage('assistant', task.result.response || t('msg-task-completed'));
+
+ // 显示执行结果摘要
+ if (task.result.execution_result) {
+ const execResult = task.result.execution_result;
+ if (execResult.status === 'success') {
+ const manifest = execResult.manifest || {};
+ const metrics = manifest.metrics || {};
+ this.addMessage('system',
+ t('msg-analysis-complete') + (metrics.final_members || 0) + t('msg-gene-members'));
+ }
+ }
+ } else if (task.status === 'failed') {
+ this.addMessage('error', task.result?.response || t('msg-task-failed'));
+ }
+ }
+ } catch (error) {
+ console.error(t('msg-check-task-status-failed'), error);
+ }
+ },
+
+ selectTask(task) {
+ this.currentTask = task.task_id;
+ this.checkTaskStatus(task.task_id);
+ },
+
+ startPolling() {
+ this.pollingInterval = setInterval(() => {
+ if (this.currentTask) {
+ this.checkTaskStatus(this.currentTask);
+ }
+ this.loadTasks();
+ }, 3000); // 每3秒轮询一次
+ },
+
+ stopPolling() {
+ if (this.pollingInterval) {
+ clearInterval(this.pollingInterval);
+ }
+ },
+
+ addMessage(role, content) {
+ this.messages.push({
+ role: role,
+ content: content,
+ timestamp: new Date().toISOString()
+ });
+
+ // 滚动到底部
+ setTimeout(() => {
+ const container = this.$refs.messageContainer;
+ if (container) {
+ container.scrollTop = container.scrollHeight;
+ }
+ }, 100);
+ },
+
+ getRoleLabel(role) {
+ const keys = {
+ 'user': 'user-role',
+ 'assistant': 'agent-role',
+ 'system': 'system-role',
+ 'error': 'error-role',
+ 'execution': 'execution-role',
+ 'planning': 'planning-role',
+ 'diagnosis': 'diagnosis-role'
+ };
+ return keys[role] ? t(keys[role]) : role;
+ },
+
+ getStatusIcon(status) {
+ const icons = {
+ 'pending': '⏳',
+ 'running': '🔄',
+ 'completed': '✅',
+ 'failed': '❌',
+ 'cancelled': '🚫'
+ };
+ return icons[status] || '❓';
+ },
+
+ formatMessage(content) {
+ // 简单的Markdown格式化
+ return content
+ .replace(/\*\*(.+?)\*\*/g, '$1 ')
+ .replace(/\n/g, ' ');
+ },
+
+ formatTime(timestamp) {
+ if (!timestamp) return '';
+ const date = new Date(timestamp);
+ return date.toLocaleTimeString('zh-CN', {
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit'
+ });
+ },
+
+ t(key) {
+ return window.t ? window.t(key) : key;
+ }
+ }
+};
+
+
+
\ No newline at end of file
diff --git a/apps/web/vue3/index-with-sidebar_ml.html b/apps/web/vue3/index-with-sidebar_ml.html
new file mode 100755
index 000000000..05f6b5d3b
--- /dev/null
+++ b/apps/web/vue3/index-with-sidebar_ml.html
@@ -0,0 +1,391 @@
+
+
+
+
+
+
+ BotanicalBaseAgent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ t('analysis-stage') }}
+
+
+
+
+
+
+
+
+ {{ t('execute-analysis') }}
+
+
+
+
+
+
{{ t('param-settings') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ msg.role === 'user' ? t('user') : t('ai-assistant') }} · {{ msg.time }}
+
+
{{ msg.content }}
+
+
+
+
{{ t('start-new-chat') }}
+
{{ t('or-continue') }}
+
+
+
+
+
+
+
+ {{ t('send') }}
+
+
+
+
+
+
+
📊 Analysis Results
+
+
+
+ {{ stage.label }}
+ {{ stage.statusText }}
+
+
+
+
+
+
+
+
+
{{ t('results-here') }}
+
+
+
+
+
+
+
+
diff --git a/apps/web/vue3/index.html b/apps/web/vue3/index.html
index ec8499c5f..56376ac03 100644
--- a/apps/web/vue3/index.html
+++ b/apps/web/vue3/index.html
@@ -1019,7 +1019,7 @@
}
.execution-log-item.assistant .log-content.structured-content {
- /* 结构化内容使用rag样式 */
+ font-size: 13px;
}
.execution-log-item.assistant .log-content.short-content {
@@ -1387,7 +1387,8 @@
-
+
拓展分析阶段
@@ -1413,10 +1414,13 @@
名称
原始命令
环境
-
-
-
-
+
+
+
原始代码
@@ -1801,6 +1805,20 @@
📂 Select
+
+
+
拖入固定结构拓展包
+
要求包含 raw_code/run.sh 与 raw_test_data/,系统会自动创建 Job、解析环境并开始 lightweight upload。
+
+
+ {{ lightweightExtensionTestStatus }}
+ 选择文件夹
+
+
+
@@ -7955,6 +7973,7 @@ RNA-seq KEGG/fgsea 会优先使用物种级 KEGG gene set;没有物种级缓
working_dir: '',
output_dir: ''
});
+ const lightweightExtensionTestStatus = ref('拖入固定结构文件夹,系统将自动创建 Job、解析环境并开始 upload。');
const activeRnaseqScopeId = ref('');
const activeGenotypeScopeId = ref('');
@@ -8131,6 +8150,7 @@ RNA-seq KEGG/fgsea 会优先使用物种级 KEGG gene set;没有物种级缓
pop_snp_density: 'SNP密度分析',
rnaseq_de: 'RNA-seq DE',
combo: '组合图',
+ gene_family_report: '基因家族结果报告',
reference_classification: '参考亚家族分类',
subfamily_confirm: '亚家族标签确认',
ppi_string_reference_confirmation: 'Confirm the STRING reference species for PPI',
@@ -9310,6 +9330,7 @@ RNA-seq KEGG/fgsea 会优先使用物种级 KEGG gene set;没有物种级缓
statusDialogVisible: false,
statusEvents: []
});
+ const extensionEnvOptions = ref(['test4py311', 'rnaseq', 'rnaR']);
const lightweightExtensions = ref([]);
const lightweightExtensionConversation = reactive({
cards: [],
@@ -10300,6 +10321,7 @@ compare: [[flesh, peel]]`;
{ name: 'pep', label: '1️⃣2️⃣ PEP', status: 'pending', images: [], files: [], elapsed_time: 0 },
{ name: 'expression', label: '1️⃣3️⃣ Expression (FPKM)', status: 'pending', images: [], files: [], elapsed_time: 0 },
{ name: 'combo', label: '6️⃣ Combo', status: 'pending', images: [], files: [], elapsed_time: 0 },
+ { name: 'gene_family_report', label: '📄 基因家族结果报告', status: 'pending', images: [], files: [], elapsed_time: 0 },
// RNA-seq stages
{ name: 'rnaseq_qc', label: '1️⃣5️⃣ RNA-seq QC', status: 'pending', images: [], files: [], elapsed_time: 0 },
{ name: 'rnaseq_align', label: '1️⃣6️⃣ RNA-seq Align', status: 'pending', images: [], files: [], elapsed_time: 0 },
@@ -15499,6 +15521,54 @@ compare: [[flesh, peel]]`;
return `⏸️ 批量执行已暂停:成功 ${completedCount}/${totalCount},暂停 ${pausedCount}/${totalCount}。真实原因:${reasonText}。`;
};
+ const openGeneratedGeneFamilyReport = async () => {
+ await refreshResults(jobInfo.job_id, 'gene_family_report');
+ const reportStage = stages.find(stage => stage.name === 'gene_family_report');
+ const reportFile = (reportStage?.files || []).find(file =>
+ file.role === 'gene_family_report' || String(file.name || '').endsWith('.gene_family_report.md')
+ );
+ if (!reportFile) return false;
+ rightPanelTab.value = 'results';
+ selectFile('gene_family_report', reportFile);
+ return true;
+ };
+
+ const submitGeneFamilyReportForBatch = async (stages, pausedCount) => {
+ if (pausedCount > 0) return false;
+ const plannedIds = (stages || []).map(stage => getStageId(stage)).filter(
+ stageId => stageId && !['summary', 'gene_family_report', 'subfamily_confirm'].includes(stageId)
+ );
+ const geneFamilyStages = new Set([
+ 'identify', 'phylogeny', 'motif', 'domain', 'structure', 'promoter',
+ 'synteny', 'interspecies_synteny', 'karyotype', 'combo', 'ppi', 'expression', 'pep'
+ ]);
+ if (!plannedIds.some(stageId => geneFamilyStages.has(stageId))) return false;
+ const stageStatuses = Object.fromEntries(plannedIds.map(stageId => {
+ const status = stageExecutionStatus.value[stageId];
+ return [stageId, status === 'completed' ? 'success' : status === 'failed' ? 'failed' : 'missing'];
+ }));
+ const batchParams = batchExecuteParams.value || {};
+ const planParams = batchParams.plan?.params || {};
+ const stageParams = (stages || []).find(stage => stage && typeof stage === 'object' && stage.params)?.params || {};
+ try {
+ await executeSingleStageWithParams('gene_family_report', {
+ expectedStages: plannedIds,
+ stageStatuses,
+ reportAction: 'generate',
+ reportUseModel: true,
+ scopeId: batchParams.scopeId || planParams.scopeId || stageParams.scopeId || '',
+ analysisRoute: batchParams.analysisRoute || planParams.analysisRoute || stageParams.analysisRoute || '',
+ memberSetId: batchParams.memberSetId || planParams.memberSetId || stageParams.memberSetId || '',
+ rnaseqScopeId: batchParams.rnaseqScopeId || planParams.rnaseqScopeId || stageParams.rnaseqScopeId || ''
+ });
+ addAgentMessage('assistant', '📄 基因家族结果报告已生成。');
+ return true;
+ } catch (err) {
+ addAgentMessage('assistant', `⚠️ 基因家族结果报告生成失败:${err.message || err}`);
+ return false;
+ }
+ };
+
const confirmAndStartBatchExecute = async (event) => {
if (!event?.isTrusted) return;
batchExecutionUserConfirmed.value = true;
@@ -15616,6 +15686,7 @@ compare: [[flesh, peel]]`;
}).length;
const totalCount = stages.length;
+ let continuationOpened = false;
if (failedCount > 0) {
addAgentMessage('assistant', `⚠️ 批量执行完成:成功 ${completedCount}/${totalCount},失败 ${failedCount}/${totalCount}`);
} else if (pausedCount > 0) {
@@ -15623,13 +15694,16 @@ compare: [[flesh, peel]]`;
} else {
addAgentMessage('assistant', `🎉 批量执行完成!成功: ${completedCount}/${totalCount}`);
if (isMethodologyDataPreparationContinuationCandidate(stages, completedCount, totalCount, failedCount, pausedCount)) {
- await openMethodologyDataPreparationContinuation();
+ continuationOpened = await openMethodologyDataPreparationContinuation();
} else if (isMethodologyPostConfirmContinuationCandidate(stages, completedCount, totalCount, failedCount, pausedCount)) {
- await openMethodologyPostConfirmContinuation();
+ continuationOpened = await openMethodologyPostConfirmContinuation();
} else if (isContextualPostConfirmContinuationCandidate(stages, completedCount, totalCount, failedCount, pausedCount)) {
- await openContextualPostConfirmContinuation();
+ continuationOpened = await openContextualPostConfirmContinuation();
}
}
+ if (pausedCount === 0 && !continuationOpened) {
+ await submitGeneFamilyReportForBatch(stages, pausedCount);
+ }
}
};
@@ -18023,6 +18097,16 @@ compare: [[flesh, peel]]`;
requestBody.geoId = confirmParams.geoId || '';
requestBody.idTransform = confirmParams.idTransform || 'remove_T01';
}
+ if (stageName === 'gene_family_report') {
+ requestBody.expectedStages = activeStageParams.expectedStages || [];
+ requestBody.stageStatuses = activeStageParams.stageStatuses || {};
+ requestBody.reportAction = activeStageParams.reportAction || 'generate';
+ requestBody.reportUseModel = activeStageParams.reportUseModel !== false;
+ requestBody.scopeId = activeStageParams.scopeId || '';
+ requestBody.analysisRoute = activeStageParams.analysisRoute || '';
+ requestBody.memberSetId = activeStageParams.memberSetId || '';
+ requestBody.rnaseqScopeId = activeStageParams.rnaseqScopeId || '';
+ }
if (stageName === 'promoter_plot_revise') {
const elements = Array.isArray(confirmParams.promoterElements) ? confirmParams.promoterElements : [];
const categories = Array.isArray(confirmParams.promoterCategories) ? confirmParams.promoterCategories : [];
@@ -18113,6 +18197,9 @@ compare: [[flesh, peel]]`;
} else if (data.async === false) {
// 同步执行完成,直接返回结果
console.log(`[DEBUG-EXEC] ${stageName}: sync completed`);
+ if (stageName === 'gene_family_report') {
+ await openGeneratedGeneFamilyReport();
+ }
return data;
} else {
throw createStructuredExecuteError(data);
@@ -19381,6 +19468,9 @@ compare: [[flesh, peel]]`;
result: data
});
await handleStageCompleted(stageName, data, {});
+ if (stageName === 'gene_family_report') {
+ await openGeneratedGeneFamilyReport();
+ }
// 更新阶段结果 - 如果有分stage的文件,则更新所有stage的文件
if (stageIdx >= 0 && data.result) {
@@ -19949,6 +20039,10 @@ compare: [[flesh, peel]]`;
};
const extensionFileKey = (file) => `${file?.name || ''}::${file?.size || 0}::${file?.lastModified || 0}`;
+ const extensionUploadPath = (file) => {
+ const relative = String(file?.lightweightRelativePath || '').trim().replace(/^\/+/, '');
+ return relative || String(file?.name || '').trim();
+ };
const appendExtensionFiles = (target, files) => {
const current = Array.isArray(target) ? target : [];
const merged = [...current];
@@ -19974,6 +20068,53 @@ compare: [[flesh, peel]]`;
if (selected.length) ElMessage.success(`已选择 ${selected.length} 个测试数据文件`);
event.target.value = '';
};
+ const handleLightweightExtensionFolderDrop = (event) => {
+ const droppedFiles = Array.from(event?.dataTransfer?.files || []);
+ if (!droppedFiles.length) {
+ ElMessage.warning('未检测到可用文件');
+ return;
+ }
+
+ const codeExtPattern = /\.(py|r|sh|bash|pl|js|ts|java|c|cc|cpp|go|rs|jl|rb|m|smk|nf|ipynb)$/i;
+ const dataExtPattern = /\.(fastq|fq|gz|fa|fasta|fna|faa|pep|cds|gff|gff3|gtf|csv|tsv|txt|xlsx|xls|vcf|sam|bam)$/i;
+
+ const normalized = droppedFiles.map((file) => {
+ const relativePath = file.webkitRelativePath || file.name;
+ if (!file.lightweightRelativePath) {
+ Object.defineProperty(file, 'lightweightRelativePath', {
+ value: relativePath,
+ configurable: true
+ });
+ }
+ return file;
+ });
+
+ const codeFiles = [];
+ const dataFiles = [];
+ normalized.forEach((file) => {
+ const filePath = extensionUploadPath(file).toLowerCase();
+ if (codeExtPattern.test(filePath)) {
+ codeFiles.push(file);
+ } else if (dataExtPattern.test(filePath)) {
+ dataFiles.push(file);
+ } else {
+ codeFiles.push(file);
+ }
+ });
+
+ extensionBuild.codeFiles = appendExtensionFiles(extensionBuild.codeFiles, codeFiles);
+ extensionBuild.dataFiles = appendExtensionFiles(extensionBuild.dataFiles, dataFiles);
+
+ if (!extensionBuild.displayName) {
+ const firstPath = normalized[0]?.webkitRelativePath || normalized[0]?.name || '';
+ const folder = firstPath.includes('/') ? firstPath.split('/')[0] : '';
+ if (folder) {
+ extensionBuild.displayName = folder.replace(/[^a-zA-Z0-9_-]+/g, '_');
+ }
+ }
+
+ ElMessage.success(`已拖入 ${normalized.length} 个文件,已自动填充拓展包`);
+ };
const removeExtensionFile = (kind, file) => {
const key = extensionFileKey(file);
if (kind === 'code') {
@@ -20006,9 +20147,10 @@ compare: [[flesh, peel]]`;
};
const extensionApiUrl = (path) => {
const value = String(path || '');
- if (!value.startsWith('/api/extension-stages') && !value.startsWith('/api/extension-stage-runs')) return value;
+ if (!value.startsWith('/api/extension-stages') && !value.startsWith('/api/extension-stage-runs') && !value.startsWith('/api/lightweight-extension-stages')) return value;
const host = window.location.hostname || '127.0.0.1';
- return `${window.location.protocol}//${host}:7880${value}`;
+ const port = window.location.port;
+ return port ? `${window.location.protocol}//${host}:${port}${value}` : `${window.location.protocol}//${host}${value}`;
};
const extensionApiFetch = async (path, options = {}) => fetch(extensionApiUrl(path), options);
const pushExtensionStatus = (message, type = 'info', showDialog = false) => {
@@ -20369,9 +20511,10 @@ compare: [[flesh, peel]]`;
form.append('extension_id', extensionId);
if (hasCommand) form.append('original_command', extensionBuild.command);
form.append('operation_token', token);
- extensionBuild.codeFiles.forEach(file => form.append('raw_code_files', file));
- extensionBuild.dataFiles.forEach(file => form.append('raw_data_files', file));
+ extensionBuild.codeFiles.forEach(file => form.append('raw_code_files', file, extensionUploadPath(file)));
+ extensionBuild.dataFiles.forEach(file => form.append('raw_data_files', file, extensionUploadPath(file)));
if (hasDataPaths) form.append('raw_data_paths', extensionBuild.dataPaths);
+ form.append('runtime_env', extensionBuild.runtimeEnv);
const response = await extensionApiFetch('/api/lightweight-extension-stages/upload', { method: 'POST', body: form });
if (!response.ok) throw new Error(await extensionApiError(response, '原始包可复现检查提交失败'));
ElMessage.success('文件上传成功,已开始生成并验收');
@@ -20616,6 +20759,18 @@ compare: [[flesh, peel]]`;
console.warn('[lightweight extension] list failed', err);
}
};
+ const loadExtensionEnvOptions = async () => {
+ try {
+ const response = await extensionApiFetch('/api/extension-stages/envs');
+ if (!response.ok) return;
+ const data = await response.json();
+ if (data.success && Array.isArray(data.envs) && data.envs.length) {
+ extensionEnvOptions.value = data.envs;
+ }
+ } catch (err) {
+ console.warn('[extension env options] load failed', err);
+ }
+ };
const answerLightweightExtensionHelp = async (message) => {
const query = String(message || '');
const matched = (lightweightExtensions.value || []).find(item => query.includes(item.extension_id));
@@ -23857,6 +24012,7 @@ compare: [[flesh, peel]]`;
startRuntimeMonitor();
loadExtensionStages();
loadLightweightExtensionStages();
+ loadExtensionEnvOptions();
resumeLightweightOperation();
loadExtensionRuns();
loadMethodologyJobs();
@@ -24189,6 +24345,7 @@ compare: [[flesh, peel]]`;
intentHistory,
uploadedFiles,
extensionBuild,
+ extensionEnvOptions,
extensionStages,
extensionCompletedStages,
lightweightExtensionConversation,
@@ -24207,6 +24364,7 @@ compare: [[flesh, peel]]`;
extensionFileKey,
onExtensionCodeFilesChange,
onExtensionDataFilesChange,
+ handleLightweightExtensionFolderDrop,
removeExtensionFile,
buildExtensionStage,
confirmLightweightCandidate,
@@ -24226,6 +24384,7 @@ compare: [[flesh, peel]]`;
validateExtensionStage,
publishExtensionStage,
loadExtensionStages,
+ loadExtensionEnvOptions,
loadExtensionRuns,
runExtensionStage,
runExtensionStageFromIntent,
@@ -24345,6 +24504,24 @@ compare: [[flesh, peel]]`;
openPreview,
redrawImage,
createNewJob,
+ lightweightExtensionTestStatus,
+ handleLightweightExtensionFolderDrop: (event) => window.LightWeightExtensionTest && window.LightWeightExtensionTest.handleDrop({
+ jobInfo,
+ extensionBuild,
+ createNewJob,
+ appendExtensionFiles,
+ buildLightweightExtensionStage,
+ lightweightExtensionTestStatus,
+ }, event),
+ handleLightweightExtensionFolderPick: (event) => window.LightWeightExtensionTest && window.LightWeightExtensionTest.handlePick({
+ jobInfo,
+ extensionBuild,
+ createNewJob,
+ appendExtensionFiles,
+ buildLightweightExtensionStage,
+ lightweightExtensionTestStatus,
+ }, event),
+ openLightweightExtensionFolderPicker: () => window.LightWeightExtensionTest && window.LightWeightExtensionTest.openPicker(),
// Working DirectorySelect
existingJobs,
jobSelectorVisible,
@@ -24461,6 +24638,9 @@ compare: [[flesh, peel]]`;
document.getElementById('app').style.display = 'block';
console.log('App mounted successfully');
+
+
+
+
+
+
+
+ Loading…
+
+
+
+
+
+
+
+
+
+
+
+
+
▶ 分析阶段 Analysis Stage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isExecuting ? '⏳ Executing...' : '🚀 Execute Analysis' }}
+
+
+
+
+
+
+
Examples
+
+
+ Download
+
+
+
+ config.xlsx (genome_prepare template / required)
+ samples.yaml (RNA-seq full workflow template)
+ geneID (??ID??)
+ sample.txt (??????-???)
+ expression.fpkm.txt (?????)
+ annotation_config.yaml (??????)
+
+
+
+
+
+
+
+
+
{{ t('方法学文献') }}
+
+
+
+
+
+
+
+ {{ t('提取方法学') }}
+
+
+ {{ t('刷新记录') }}
+
+
+
+
+ {{ methodologyLatestJob.status === 'completed' ? t('打开最新提取结果') : t('查看最新提取状态') }}
+
+
+
+
+
+
+ {{ methodologySelectedJob.status === 'completed' ? t('打开所选提取结果') : t('查看所选提取状态') }}
+
+
+
+ {{ methodologyStatusText }}
+
+
+
+
+
+
+
+
+ {{ t('拓展分析阶段') }}
+
+ {{ t('运行结果') }}
+
+
+
{{ t('已写阶段') }}
+
+
+
+
+
{{ t('名称') }}
+
{{ t('原始命令') }}
+
{{ t('环境') }}
+
+
+
+
+
+
{{ t('原始代码') }}
+
+
+
+ {{ file.name }}
+ {{ t('移除') }}
+
+
+
{{ t('测试数据') }}
+
+
+
+ {{ file.name }}
+ {{ t('移除') }}
+
+
+
{{ t('服务器测试数据路径') }}
+
+
+
+
+
+ {{ t('生成并发布') }}
+
+
+ {{ t('运行已发布') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('分析家族') }}
+
+
+ {{ t('目标物种') }}
+
+
+ {{ t('主参考物种') }}
+
+
+
+
+
+ Identify BLAST参考
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ autofillState.rows && autofillState.rows.length ? '重新识别' : '生成草稿' }}
+ {{ t('添加行') }}
+ 检查 DE 设计
+ {{ t('确认并写入正式配置') }}
+ {{ t('取消') }}
+ confidence: high 可确认,medium 建议检查,low 必须人工补全;正式写入前会自动备份旧文件。
+
+
+
+ {{ autofillState.type === 'samples' ? 'samples.yaml 批量修订区' : '配置批量修订区' }}:筛选 → 填值 → 预览 → Apply
+ 显示 {{ autofillVisibleRows.length }} 行;匹配 {{ autofillBulkMatchedCount }} / {{ autofillState.rows.length }} 行
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('预览到表格') }}
+ Apply 确认填入
+ {{ t('取消预览') }}
+
+
+
{{ autofillBulkPreviewText }}
+
{{ autofillState.bulkLastSummary }}
+
+
+
+
+
+ {{ autofillState.designReview.module }} {{ autofillState.designReview.module_name }} ;模型 {{ autofillState.designReview.design_formula }}
+
+ Batch
+ {{ autofillState.designReview.batch.display }}
+
+ 比较主轴: {{ autofillState.designReview.comparison_axis }};按 {{ autofillState.designReview.stratify_axis }} 分层
+ 将生成的比较:
+ • {{ item.display }}
+ • {{ autofillState.designReview.interaction.display }}(LRT + Wald)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('预览') }} {{ t('已应用') }}
+
+
+ {{ t('删除') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('预览') }} {{ t('已应用') }}
+
+ {{ t('删除') }}
+
+
+ 字段说明:
+ Original_File_Name 必须匹配上传文件名;Species_Code 是物种简称;Species_Name 只填拉丁名/别名,文章复现时为必填项;Gene_Family 只在已知家族参考序列行必填;
+ File_Type 使用 genome/gff/cds/pep;Ref 是兼容旧流程的 TRUE/FALSE;
+ Species_Role 表示 target、reference、comparative、outgroup 或 seed_reference;
+ File_Scope 表示 whole_genome 或 known_family;
+ 上方“主参考物种”和“Identify BLAST参考”是参考配置主入口,会分别写入 Analysis.Primary_Reference_Species 与 Analysis.Identify_Blast_Reference_Species。
+ Reference_Use 仅作为行级高级覆盖或旧配置兼容,通常留空。
+
+
+ 字段说明:
+ run 是测序运行号/SRR号,用于匹配 FASTQ;sample_id 是样本信息表中的样本名,也是 samples.yaml 的样本键;fastq_1/fastq_2 是双端测序文件;condition 表示处理/组织/分组;replicate 是生物学重复编号。
+ time/stage 只在时间序列或明确发育时期实验中填写,普通组织比较请留空;library_strandness 必须按建库说明填写 non_stranded / forward / reverse;分组角色用于标记 control/baseline、compare group 或 ignore,DE 会按设计生成对照比较或多组两两比较。
+
+
+
+
+
+
+
{{ extensionBuild.extensionId }}
+
+
{{ t('参数') }}
+
+
+
+
+
+
+
+
{{ t('输出') }}
+
+
+
+ {{ formatSize(row.size || 0) }}
+
+
+
+
{{ extensionBuild.previewCommand }}
+
+
+ {{ t('取消') }}
+ {{ t('确认保存') }}
+
+
+
+
+
+
{{ t('暂无状态记录') }}
+
+
+ {{ event.type }}
+ {{ event.time }}
+
+
{{ event.message }}
+
+
+
+
+
+
+
+ {{ extensionRunParameterState.displayName || extensionRunParameterState.extensionId }}
+
+
+
+
+
+
+
+
+
+
+ {{ row.spec && row.spec.type || 'string' }}
+
+
+ {{ row.spec && row.spec.description || '' }}
+
+
+
+
+ {{ extensionRunParameterState.previewCommand }}
+
+
+
+ {{ t('取消') }}
+ {{ t('预览命令') }}
+ {{ t('确认并执行') }}
+
+
+
+
+
+
+ 当前 job: {{ jobInfo.job_id }}
+ {{ t('刷新结果') }}
+
+
{{ t('暂无拓展分析阶段结果') }}
+
+
+
+ {{ run.display_name || run.extension_name }} / {{ run.run_id }} / {{ run.status }}
+
+ {{ run.workspace }}
+
+
+ {{ extensionOutputIcon(out) }} {{ out.name }}
+ {{ formatSize(out.size || 0) }}
+
+
+ {{ run.error }}
+
+
+
+
+
+
+
+
+
📂 Working Directory
+
+
+ Job:
+ {{ jobInfo.job_id }}
+
+
+
+
+
+ Input:
+ {{ jobInfo.input_dir }}
+
+
+
+
+
+ Output:
+ {{ jobInfo.output_dir }}
+
+
+
+
+
+
+
+ ➕ New
+
+
+ 📂 Select
+
+
+
+
+
拖入固定结构拓展包
+
要求包含 raw_code/run.sh 与 raw_test_data/,系统会自动创建 Job、解析环境并开始 lightweight upload。
+
+
+ {{ lightweightExtensionTestStatus }}
+ 选择文件夹
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Execution Runtime
+
+
+ Gene-Family {{ geneFamilyQueuesOnline ? 'online' : 'offline' }}
+
+
+ Rgene GF {{ geneFamilyWorkerCount }}
+
+
+ RNA-seq {{ rnaseqQueueOnline ? 'online' : 'offline' }}
+
+
+
+ {{ t('重启 Celery') }}
+
+
+
+
+ {{ t('重启项目') }}
+
+
+
+
+
+ {{ runtimeActiveTaskCount }} active
+
+
+ CPU {{ runtimeCpuText }}
+
+
{{ runtimeMonitorStatusText }}
+
+
+
+
+ Tokens {{ tokenTotalText }}
+ Calls {{ tokenCallText }}
+ Cost {{ tokenCostText }}
+
+
+
+
+
+
+
+ Prefix
+ {{ runtimeOps.queue_prefix || 'unknown' }}
+
+
+ Redis
+ {{ runtimeOps.redis_url || 'unknown' }}
+
+
+ Script
+ {{ runtimeOps.start_script_exists ? 'configured' : 'missing' }}
+
+
+
+
+
+
Gene-Family Queues
+
+
+
+ Queue
+ {{ runtimeQueueLabel(queue.name) }}
+
+
+ Workers
+ {{ queue.worker_count }} worker
+
+
+ Active
+ {{ queueActiveTaskText(queue) }}
+
+
+
+
No Gene-Family queue data
+
+
+
+
+
+
RNA-seq Queues
+
+
+
+ Queue
+ {{ runtimeQueueLabel(queue.name) }}
+
+
+ Workers
+ {{ queue.worker_count }} worker
+
+
+ Active
+ {{ queueActiveTaskText(queue) }}
+
+
+
+
No RNA-seq queue data
+
+
+
+
+
Resource Limits
+
+
+ Machine threads
+ {{ machineThreadCount }}
+
+
+ Analysis threads
+
+
+
+ Max running tasks
+
+
+
+
+
+
+
Gene-family
+
+
+
+ Project CPU cap
+
+
+
+
+ Default 是新提交任务使用的线程数;Max 是该阶段上限。直接提高 Default 时会同步提高 Max。
+
+
+
+
+
+ {{ row.label }}
+ updateStageMaxCpu(row, value)" aria-label="Max CPU">
+ updateStageDefaultCpu(row, value)" aria-label="Default CPU">
+
+
+
+
+
+
+
+
RNA-seq
+
+
+
+ Project CPU cap
+
+
+
+ QC sample parallel
+
+
+
+ Align sample parallel
+
+
+
+
+ Default 是新提交任务使用的线程数;Max 是该阶段上限。样本并行数只影响 QC/Align 的样本级并发。
+
+
+
+
+
+ {{ row.label }}
+ updateStageMaxCpu(row, value)" aria-label="Max CPU">
+ updateStageDefaultCpu(row, value)" aria-label="Default CPU">
+
+
+
+
+
+
+
+ Apply to newly submitted tasks
+
+
+
+
+
+
+
💬 {{ t('当前会话') }}:
+
+
+
+
+
+ {{ conv.title }}
+
+
+
+
+
+
+
+
+
+
+ {{ t('更多会话') }}
+
+
+
+
+
+ {{ conv.title }}
+
+
+
+
+
+
+ {{ t('新建会话') }}
+
+
+
+
+
+
+
+ {{ t('新建') }}
+
+
+
+
+
+
+
+
+
Welcome — multi-omics assistant
+
+ Identification, phylogeny, motifs, domains, RNA-seq, and more.
+ Tip: describe your goal (default prefix At ).
+
+
+
+
+
+
+
+
+
+
+
AI
+
+
{{ msg.time }}
+
+
+
+ {{ t('查看本次知识检索过程') }}
+
+ ✓ {{ step.message }}
+
+
+
+ 📚 参考文献
+
+
+
+
+
+
+
+
+
+
+ {{ ragProgress.current || '正在知识库中查找相关内容……' }}
+
+ {{ t('stop-waiting') }}
+
+
+
+
+
+ {{ pidx === ragProgress.events.length - 1 ? '●' : '✓' }}
+
+ {{ step.message }}
+
+
+
+
+
+
+
AI
+
+
+
+
{{ card.title }}
+
{{ card.purpose }}
+
+
{{ card.status_label || card.status }}
+
+
{{ card.summary }}
+
+
+
+
+
{{ card.collapsed_summary }}
+
+
+ {{ input.key }}
+
+
+ {{ parameter.label }}
+
+
+ {{ t('输出前缀') }}
+
+
+
{{ card.error }}
+
+ Yes,确认无误
+ {{ t('取消') }}
+ {{ t('提出修订意见') }}
+ {{ t('重新生成说明') }}
+ {{ t('开始验证') }}
+ 结果无误,继续
+ {{ t('授权发布') }}
+ {{ t('确认执行') }}
+
+
+
+
+
+
+
+
+ {{ nlStatusText || '正在处理请求...' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
⏳
+
+
+ {{ executionIndicatorStage.toUpperCase() }} Executing
+
+
+ ⏱️ {{ executionIndicatorElapsed }}s | {{ executionIndicatorStatusText }}
+
+
+
+
+ ⏹ Stop
+
+ Polling
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t(group.label) }}
+ {{ group.fileCount }} files
+
+
+
+
+
+
+
+
+
+ 🔗 MCScanX HTML Results
+
+ {{ mcscanxHtmlFiles.length }} files
+
+
+
+
+ 🌐 {{ f.name }}
+ {{ formatSize(f.size) }}
+
+
+
+
+
+
+
+
+
+
+ {{ t(stage.label) }}
+ (Time: {{ stage.elapsed_time }}s)
+
+ {{ resultVisibleCount(stage) }} files
+
+
+
+
+
HMM/Pfam 审计
+
Requested: {{ listText(stage.hmm_audit.requested_pfams, '未记录') }}
+
Loaded: {{ listText(stage.hmm_audit.loaded_accessions, '未记录') }}
+
Missing: {{ listText(stage.hmm_audit.missing_requested_pfams) }}
+
HMM: {{ stage.hmm_audit.hmm_members || 0 }} | BLAST: {{ stage.hmm_audit.blast_members || 0 }} | Candidate: {{ stage.hmm_audit.candidate_members || 0 }} | Final: {{ stage.hmm_audit.final_members || 0 }}
+
+
+
+
+ PPI Reset
+
+
+
+
+
+
+ 🖼️ {{ getFileName(img) }}
+
+
+
+
+
+ {{ treeReplotGroupTitle(stage) }}
+
+
+ {{ isImageFile(f.name) ? '🖼️' : '📄' }} {{ f.name }}
+ {{ formatSize(f.size) }}
+
+
+
+
+
+ {{ circosReplotGroupTitle(stage) }}
+
+
+ {{ isImageFile(f.name) ? '🖼️' : '📄' }} {{ f.name }}
+ {{ formatSize(f.size) }}
+
+
+
+
+
+ {{ comboReplotGroupTitle(stage) }}
+
+
+ {{ isImageFile(f.name) ? '🖼️' : '📄' }} {{ f.name }}
+ {{ formatSize(f.size) }}
+
+
+
+
+
+
+ 自动保守区 MSA 图 ({{ stage.msa_groups.conserved.length }})
+
+
+
+ {{ isImageFile(f.name) ? '🖼️' : '📄' }} {{ f.name }}
+ {{ formatSize(f.size) }}
+
+
+
+
+
+
+
+
+ MSA区间图 ({{ stage.msa_files.length }})
+
+
+
+ {{ isImageFile(f.name) ? '🖼️' : '📄' }} {{ f.name }}
+ {{ formatSize(f.size) }}
+
+
+
+
+
+
+
+
+ PPI Reset 结果 ({{ stage.reset_groups.length }})
+
+
+
+
+
+ {{ t(group.label || group.reset_id) }}
+ 输入 {{ group.metrics.input_gene_count || 0 }} / 删除 {{ group.metrics.deleted_count || 0 }} / 新增 {{ group.metrics.added_count || 0 }}
+
+
+
+
+ 🖼️ {{ getFileName(img) }}
+
+
+ {{ isJumpFile(f.name, stage.name) ? '🔗' : '📄' }} {{ f.name }}
+ {{ formatSize(f.size) }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ isImageFile(f.name) ? '🖼️' : (isJumpFile(f.name, stage.name) ? '🔗' : '📄') }}
+ {{ f.name }}
+ {{ getArtifactRoleLabel(f.role) }}
+ (open STRING)
+ (open)
+
+ {{ formatSize(f.size) }}
+
+
+ No files
+
+
+ 暂无结果
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Select an image to preview
+
+
+
+
+
+
+ Refresh
+
+
+
+
+
+
+
+
+
+ 🔍
+
+ 🔄 {{ t('重新加载') }}
+ ⟲ {{ t('重置视图') }}
+
+
+ ⛶ Full screen
+ 📄 过滤全库页
+
+
+
+
+
节点: {{ kgStats.nodes }} | 关系: {{ kgStats.edges }} | 类型: {{ kgStats.nodeTypes }}
+
+ {{ kgSelectedNode.name }} ({{ kgSelectedNode.category }})
+ 连接数: {{ kgSelectedNode.degree }}
+
+
RAG loads a subgraph; Reload pulls the full JSON (can be slow). Double-click the graph for fullscreen.
+
+
+
+
+
+
+
+
+
+
+
+
+ 📊 图谱统计
+
+
+ 节点总数: {{ kgStats.nodes }}
+
+
+ 关系总数: {{ kgStats.edges }}
+
+
+ 节点类型: {{ kgStats.nodeTypes }}
+
+
+
+
+ 🔍 节点搜索
+
+ 🔍
+
+
+
+ 📋 节点详情
+
+
+
{{ t('节点名称') }}
+
{{ kgSelectedNode.name }}
+
+
+
{{ t('节点类型') }}
+
{{ nodeTypeLabels[kgSelectedNode.category] || kgSelectedNode.category }}
+
+
+
{{ t('连接关系数') }}
+
{{ kgSelectedNode.degree }} 个
+
+
+
+
+ 🎨 节点图例 (PC语义)
+
+
+
+
{{ nodeTypeLabels[type] || type }}
+
+
+
+
+
+
+
+ {{ t('关闭') }}
+ {{ t('重置视图') }}
+
+
+
+
+
+
+
+
+
{{ t('基础参数') }}
+
+
+
{{ isGwasStage(confirmParams.stage) ? '物种简称(用于结果前缀)' : '输出标签 / Prefix' }}
+
+
+ {{ outputLabelAutoMessage(confirmParams.stage) }}
+
+
+
+ 本阶段 CPU 线程数
+
+
+
+ {{ t('样本并行数量') }}
+
+ {{ t('同时处理的样本数') }}
+
+
+
+
+
+
RNA-seq 分析链路确认
+
+
统计链: HISAT2 BAM -> featureCounts raw fragment counts -> DESeq2 -> DE genes -> GO/KEGG/FGSEA/WGCNA/candidate。
+
展示链: Salmon/tximport TPM 只在 bridge QC 通过后进入 expression plots;tximport counts_from_abundance 不作为默认 DESeq2 输入。
+
样本配置: {{ confirmParams.rnaseqSampleConfig || '自动使用 input/final 中的 samples.yaml' }}
+
+
+
+
+
RNA-seq QC 工具参数
+
+
+ QC 工具
+
+
+
+
+
+
+ fastp 可执行文件
+
+ 留空时使用 FASTP_BIN 或 PATH
+
+
+
+ 自动检测 PE adapter
+
+
+
+ cut front
+
+
+
+ cut tail
+
+
+
+ window size
+
+
+
+ mean quality
+
+
+
+ qualified phred
+
+
+
+ unqualified %
+
+
+
+ N base limit
+
+
+
+ length required
+
+
+
+
+
+
+
RNA-seq Align 参数
+
+
+ Known splice site 来源
+
+
+
+
+
+
+
+
+ splice site / 注释路径
+
+
+
+
+
+
+
RNA-seq 定量参数
+
+
+ {{ t('定量模式') }}
+
+
+
+
+
+
+ {{ t('丰度引擎') }}
+
+
+
+
+
+
+
+
+ Salmon libType
+
+
+
+ tximport counts
+
+
+
+
+
+
+
+
+ Transcriptome FASTA
+
+
+
+ tx2gene TSV
+
+
+
+ Kallisto bias
+
+
+
+
+ featureCounts raw fragment counts 是后续 DESeq2 的默认证据;Salmon/tximport TPM 用于表达展示,只有样本名和 gene ID overlap 检查通过后才会给 expression plots 使用。
+
+
+
+
+
RNA-seq 差异分析参数
+
+
+ padj 阈值
+
+
+
+ |log2FC| 阈值
+
+
+
+ {{ t('实验设计模块') }}
+
+
+
+
+
+
+
+
+
+
+ 设计审查: {{ confirmParams.rnaseqDesignReview.module }} - {{ confirmParams.rnaseqDesignReview.module_name }};{{ confirmParams.rnaseqDesignReview.design_formula }}
+ 批次: {{ confirmParams.rnaseqDesignReview.batch.display }}
+ • {{ item.display }}
+ • {{ confirmParams.rnaseqDesignReview.interaction.display }}(LRT + Wald)
+
+
{{ confirmParams.rnaseqDesignReview.error }}
+
+
+ DESeq2 默认输入为 featureCounts raw fragment counts;padj 与 |log2FC| 只影响显著 DE gene 筛选,不改变原始 count matrix。
+
+
+
+
+
RNA-seq 富集高级参数
+
+
+ pvalue cutoff
+
+
+
+ qvalue cutoff
+
+
+
+ {{ t('背景来源') }}
+
+
+
+
+
+
+
+
+ top pathways
+
+
+
+ OrgDb species
+
+
+
+ keyType
+
+
+
+ GO OBO
+
+
+
+ KEGG level
+
+
+
+
+
+
+ KEGG filter
+
+
+
+
+
+
+ Highlight mode
+
+
+
+
+
+
+
+ KEGG organism
+
+
+
+ Plot type
+
+
+
+
+
+
+ 禁用 KEGG 网络
+
+
+
+ 跳过 pathview
+
+
+
+
+
+
+
RNA-seq FGSEA 参数
+
+
+ comparison
+
+
+
+ min size
+
+
+
+ max size
+
+
+
+ permutations
+
+
+
+ seed
+
+
+
+ KEGG organism
+
+
+
+
+ FGSEA 使用 DESeq2 统计量排序,并使用 gene2ko/KEGG gene set;comparison 留空时自动选择可用比较。
+
+
+
+
+
RNA-seq WGCNA 参数
+
+
+ {{ t('表达矩阵') }}
+
+
+
+ count matrix
+
+
+
+ sample table
+
+
+
+ min mean
+
+
+
+ max genes
+
+
+
+ min module size
+
+
+
+ merge cut height
+
+
+
+ powers
+
+
+
+
+
+
+
RNA-seq Candidate 参数
+
+
+ max DE proteins
+
+
+
+ InterProScan
+
+
+
+
+
+
+
+ InterProScan applications
+
+
+
+
+
+
+
+
+
+
+ InterProScan 需要本机可执行文件和 Java 环境;auto 模式只在缺少可用功能注释且环境可用时运行。
+
+
+
+
+
{{ confirmParams.stage === 'identify' ? '本阶段成员鉴定对象' : '本阶段执行范围' }}
+
+
+
Family
+
{{ confirmParams.geneFamily || '未设置' }}
+
+
+
Scope
+
{{ scopeModeLabel(confirmParams.scopeMode) }}
+
+
+
{{ confirmParams.stage === 'identify' ? '将鉴定物种' : 'target 目标物种' }}
+
{{ listText(confirmParams.stage === 'identify' ? identifyExecutionSpecies(confirmParams) : confirmParams.targetSpecies) }}
+
+
+
reference 参考物种
+
{{ listText(confirmParams.referenceSpecies, '无') }}
+
+
+
{{ t('后续主参考物种') }}
+
{{ listText(confirmParams.pipelinePrimaryReferenceSpecies, '未设置') }}
+
+
+
Identify BLAST参考
+
{{ listText(confirmParams.pipelineIdentifyBlastReferenceSpecies, '未设置') }}
+
+
+
comparative 比较物种
+
{{ listText(confirmParams.comparativeSpecies, '无') }}
+
+
+
+ 这里是 identify 的执行集合;不会改变 config 中 target / comparative / reference 的数据角色。
+ Identify BLAST参考:{{ listText(confirmParams.pipelineIdentifyBlastReferenceSpecies) }}。
+ 后续主参考:{{ listText(confirmParams.pipelinePrimaryReferenceSpecies) }}。
+
+
+
+ {{ t('仅目标') }}
+ 目标+参考
+ 目标+比较
+ {{ t('全部') }}
+
+
+
+
+ Family
+
+
+
+
+
+ {{ t('主参考') }}
+
+
+
+ BLAST参考
+
+
+
+
target 目标物种
+
+ {{ sp }}
+
+
+
+
reference 参考物种(仅用于标签迁移)
+
+ {{ sp }}
+
+
+
+
comparative 比较物种(参与比较/分类,不改为 target)
+
+ {{ sp }}
+
+
+
+
+
+
+
Genome Prepare 参数
+
+
+
+
+
+
+
+
+
+
+
+
📦 Input文件要求(支持.gz压缩):
+
+
JGI: *protein_primaryTranscriptOnly.fa + *gene_exons.gff3 (+ *genome.fa)
+
Ensembl: *.pep.all.fa + *.gff3 (+ *.cds.all.fa)
+
NCBI: *_protein.faa + *_genomic.gff (+ *_cds_from_genomic.fna)
+
+
+ 💡 智能提取: 如果没有蛋白文件,但有 genome.fa + GFF,系统会自动用 gffread 提取蛋白序列
+
+
+ ✅ Output文件将保存到 input/{job_id}/ 目录供后续分析使用
+
+
+
+
+
+
NCBI序列下载
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
按ID提取序列
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Identify 参数
+
+
{{ t('本次成员鉴定物种') }}
+
+
目标物种(默认勾选)
+
+
+ {{ item.species }}(未检测到蛋白FASTA)
+
+
+
+
+
比较物种(按需勾选)
+
+
+ {{ item.species }}(未检测到蛋白FASTA)
+
+
+
+
+
参考物种(仅参考,不参与目标鉴定)
+
+ {{ item.species }}
+
+
+
+ 当前鉴定:{{ listText(identifyExecutionSpecies(confirmParams)) }}
+
+
+
+
+
+ hmmsearch domain i-Evalue
+
+
+
+
+ HMM Coverage 0-1
+
+
+
+
+
+
+ Blast E-value
+
+
+
+
+ Blast Identity %
+
+
+
+
+
+
+ Required Pfams
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Phylogeny 参数
+
+
+
+ MAFFT
+
+
+
+
+
+
+ IQ-TREE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Combo 修订参数
+
+
当前图片:{{ getFileName(confirmParams.comboSourceImage) }}
+
选择要在 combo 图中展示的分析类型;单类型图请使用对应阶段已有结果。
+
+
+
+ Motif
+ Gene structure
+ Domain
+
+
+
+ 至少选择两个类型;默认三类全选。重绘会复用已完成的 phylogeny/motif/structure/domain 结果,不重新运行上游分析。
+
+
+
+
+
{{ t('启动子自定义绘图') }}
+
+
复用已完成 promoter 阶段的 PlantCARE filtered 表,不重新提交 PlantCARE。
+
+
+
+ {{ t('输出标签') }}
+
+
+
+
{{ t('启动子元件') }}
+
+
+
+
+
+
{{ t('功能类别') }}
+
+ {{ item.category }} ({{ item.count }})
+
+
+
+
+
{{ t('匹配方式') }}
+
+
+
+
+
+
+
+
{{ t('图例颜色') }}
+
+
+
+
+
+
+
+
+
+
{{ t('图类型') }}
+
+ {{ t('位置分布图') }}
+ Heatmap
+ Barplot
+
+
+
+
+
+
种内共线性 Circos 修订参数
+
+
当前图片:{{ getFileName(confirmParams.syntenySourceImage) }}
+
绘图数据:复用 synteny 阶段的 GFF、MCScanX collinearity、family gene ID 和 genome。
+
+
+
+ {{ t('输出标签') }}
+
+
+
+
+
+
+ 基因ID距离参数对应 Circos text plot 的 r0;数值越小越靠近染色体色块。字号增大时系统会自动加宽外侧文字轨道,避免 Circos 丢弃标签。默认不覆盖原图,生成 synteny_replot_circos 文件。
+
+
+
+
+
MSA区间绘图参数
+
+
当前图片:{{ getFileName(confirmParams.msaSourceImage) }}
+
MSA来源:{{ confirmParams.msaFile }}
+
MSA来源:将根据当前图片/manifest自动解析
+
+
+
+ 推荐保守区:alignment {{ confirmParams.msaRecommendedStartCol }}-{{ confirmParams.msaRecommendedEndCol }}
+ score={{ Number(confirmParams.msaRecommendedScore || 0).toFixed(3) }}, non-gap={{ Number(confirmParams.msaRecommendedNongap || 0).toFixed(3) }}
+
+
{{ t('使用推荐区间') }}
+
+
+ 正在分析当前 MSA 的保守区推荐...
+
+
+
+ {{ t('手动区间') }}
+ {{ t('自动保守窗口') }}
+
+
+
+
+
+ 默认读取 final/{{ confirmParams.prefix }}.pep.msa.fa;位点为 alignment 列坐标,包含缺口列。
+
+
+
+
+
{{ t('亚家族确认参数') }}
+
+
+
+ {{ t('参考分类') }}
+
+
+
+
+
+
+ {{ t('目标前缀') }}
+
+
+
+
+ {{ t('参考前缀') }}
+
+
+
+
+
+ 覆盖 final/{{ confirmParams.prefix }}.geneID.subfamily 兼容文件
+
+
+
+
+
+
Motif 参数
+
+
+
+ Motif数量
+
+
+
+
+
+
+ {{ t('最小宽度') }}
+
+
+
+
+ {{ t('最大宽度') }}
+
+
+
+
+
+
+
Domain 参数
+
+
+
+ hmmscan E-value
+
+
+
+
+ Coverage %
+
+
+
+
+
+
+
TFBS 参数
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('基因家族 promoter 扫描') }}
+ 同时跑全注释基因 promoter 背景
+
+
+
+
+
+
+
+
+
+ {{ t('扫描双链') }}
+
+
+
+
+
+
Expression 参数 (FPKM/TPM矩阵)
+
+
+
+ GEO编号
+
+
+
+
+
+
+
+
+
+
+
+
+
+
📊 表达量数据来源(Select一种):
+
+
✅ 方式1: 直接上传 - 上传表达量矩阵(*.fpkm.txt/*.tpm.txt)+基因ID+sample.txt到Input目录
+
✅ 方式2: GEO编号 - InputGEO编号(如 GSE111937)自动Download
+
+
+
+
+
+
+
RNA-seq 参数
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('物种名') }}
+
+
+
+
+
🔬 RNA-seq 数据来源:
+
+
+ ✅ 上传模式: 需要上传以下文件到Input目录:
+ • 参考Genome (*.fa/*.fasta)
+ • 基因注释 (*.gtf/*.gff)
+ • 样本配置文件 (samples.yaml)
+ • 原始测序文件 (*_R1.fastq.gz, *_R2.fastq.gz)
+
+
+ 🔍 检索Download模式: Input物种拉丁名后,系统会自动搜索并返回参考Genome和GTF的FTPDownload链接
+
+
+
+
+
+
+
RNA-seq 物种信息(KEGG/fgsea/富集用)
+
+
+
+ {{ t('拉丁名') }}
+
+
+
+
+
+
+ KEGG organism
+
+
+
+
+ KEGG/fgsea 会优先复用本地物种级 KEGG 缓存;没有缓存时尝试 KEGG REST 下载。若 KEGG 无该物种或网络不可用,则回退到 KO 全库并在构建 gene set 前过滤人类疾病和动物系统通路。
+
+
+
+
+
+
RNA-seq QC 参数 (Quality Control - Step 1)
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('数据路径') }}
+
+
+
+
+
+ QC 工具
+
+
+
+
+
+
+ fastp 可执行文件
+
+
+
+
+ 自动检测 PE adapter
+
+
+
+ cut front
+
+
+
+ cut tail
+
+
+
+ window size
+
+
+
+ mean quality
+
+
+
+ qualified phred
+
+
+
+ unqualified %
+
+
+
+ N base limit
+
+
+
+ length required
+
+
+
+
+
+
📋 所需文件:
+
+ • 样本配置文件
samples.yaml
+ • 原始测序文件
*_R1.fastq.gz, *_R2.fastq.gz
+ • 参考Genome
*.fa/*.fasta
+ • 基因注释
*.gtf/*.gff
+
+
💡 可通过上方"Download Examples文件"获取samples.yaml模板
+
+
+ • 数据路径下需包含:
samples.yaml +
FASTQ文件
+ • input目录仍需上传:
参考Genome +
{{ t('基因注释') }}
+ • 支持多个路径(逗号分隔),自动合并所有FASTQ文件
+ • 系统将自动创建FASTQ文件的软链接
+
+
💡 适合大数据量/多组织/多时期,避免重复上传
+
+
+
+
+
+
+
RNA-seq Align 参数 (Alignment - Step 2)
+
+
+ Known splice site 来源
+
+
+
+
+
+
+
+
+ splice site / 注释路径
+
+
+
+
+
✅ 自动使用上一步Output:
+
+ • 使用 QC Output的 clean reads
+ • 使用上一步的参考Genome和GTF
+
+ 无需再次Upload Files
+
+
+
+
+
+
+
RNA-seq Quant 参数 (Quantification - Step 3)
+
+
✅ 自动使用上一步Output:
+
+ • 使用 Align Output的 BAM 文件
+ • 使用上一步的 GTF 注释
+
+ 无需再次Upload Files
+
+
+
+
+
+
+
RNA-seq DE 参数 (差异表达 - Step 4)
+
+
+ {{ t('前景基因') }}
+
+
+
+
+
+
+
✅ 自动使用上一步Output:
+
+ • 使用 Quant Output的表达量矩阵
+ • 使用 samples.yaml 中的分组信息
+
+ 无需再次Upload Files
+
+
+
+
+
+
+
RNA-seq Enrich 参数 (Enrichment Analysis - Step 5)
+
+
+ {{ t('富集类型') }}
+
+
+
+
+
+
+
+ {{ t('前景基因') }}
+
+
+
+
+
+
+
+ {{ t('背景来源') }}
+
+
+
+
+
+
+
+
+ Applications
+
+
+
+
+
+
+
+
+
+ OrgDb物种
+
+
+
+
+
+
+
+
+
+
+ keyType
+
+
+
+
+ KEGG organism
+
+
+
+
⚠️ 自动注释说明:
+
+ • GO 缺背景时优先用 AnnotationHub/biomaRt 生成 gene2go;不可用时回退 InterProScan
+ • KEGG 缺背景时仍用 KofamScan 生成 gene2ko;该部分可能耗时较长
+ • 所需背景生成后会继续执行 RNA-seq 富集分析
+
+
+
+
✅ 自动使用上一步Output:
+
+ • 基因家族模式使用 family_DE_genes;全基因组模式使用 DE_genes
+
+ 无需再次Upload Files
+
+
+
+
+
+
PPI 参数 (Protein Interaction)
+
+
+ {{ t('物种') }}
+
+
+
+
+
+
+
+
+
+
+ {{ t('置信度阈值') }}
+
+
+
+
✅ 自动使用 Identify Output:
+
+ • 使用基因家族 ID 列表查询 STRING 数据库
+ • 置信度越高,互作关系越可靠
+ 需要先运行 Identify 阶段
+
+
+
+
📤 同源映射文件(可选):
+
+ • 如需查询非模式植物,请上传同源映射文件到 input 目录
+ • 文件名:ortholog_pairs.txt 或 At_Os_orthologs.txt
+ • 格式:query_gene_id\treference_gene_id(制表符分隔)
+ • 系统会自动搜索 workspace/kaks_analysis/ 目录下的映射文件
+
+
+
+
+
+
Ka/Ks 参数 (KaKs Analysis)
+
+
+ {{ t('中性替换率') }}
+
+
+
+
📤 需要Upload Files:
+
+ • cds1.fasta - 物种1 CDS序列 (如 At.cds.fasta)
+ • cds2.fasta - 物种2 CDS序列 (如 Os.cds.fasta)
+ • pep1.fasta - 物种1蛋白序列 (如 At.pep.fasta)
+ • pep2.fasta - 物种2蛋白序列 (如 Os.pep.fasta)
+ • ortholog_pairs.txt - 同源基因对
+ 系统会自动搜索以下位置的文件:
+ • input/ 目录
+ • workspace/<job>/kaks_analysis/ 目录
+ • 支持格式:gene_id_1\tgene_id_2(制表符分隔)
+
+
+
+
+
+
Interspecies species selection
+
Loading species from config.xlsx...
+
+
Targets are selected by default. Comparative/outgroup species are treated as reference comparison species; legacy Ref=True is still supported.
+
+
Target species (Species_Role=target or legacy Ref=False)
+
+ {{ sp }}
+
+
No target species found
+
+
+
Comparison/reference species (Species_Role=comparative/outgroup or legacy Ref=True)
+
+ {{ sp }}
+
+
No reference species found
+
+
+
BLAST engine
+
+ {{ engine.label }}
+
+
+ diamond is the default fast engine. blastp uses NCBI BLAST+. Both write outfmt 6 for MCScanX.
+
+
+
+
{{ t('绘图模式') }}
+
+ {{ mode.label }}
+
+
+
+
Target species : target species from config.xlsx; unchecked species are excluded from the tree and pairwise synteny.
+
Comparison/reference species : comparative/outgroup or legacy Ref=True species. If no curated family FASTA exists, run identify on their whole proteomes first so final_members can be split by species.
+
BLAST engine : whole-proteome pairwise search used by MCScanX. Selected: {{ confirmParams.blastEngine }}.
+
Auto-run : runs after 40 seconds with the current selection, or click confirm to run immediately.
+
Current selection: targets {{ confirmParams.targetSpecies.join(', ') || 'none' }}; references {{ confirmParams.refSpeciesList.join(', ') || 'none' }}; plot mode {{ confirmParams.interspeciesPlotMode || 'both' }}.
+
+
+
+
+
+ 💡 提示: 以上参数可根据具体数据进行调整。如不调整,则使用默认参数。
+
+
+
+
+
+
+
+
+
+
+
+
{{ autoAnnotationDialogMessage(autoAnnotationConfirmData) }}
+
+
{{ t('原因') }} : {{ autoAnnotationReasonLabel(autoAnnotationConfirmData) }}
+
{{ t('需要补充') }} : {{ autoAnnotationRequiredBackgrounds(autoAnnotationConfirmData) }}
+
{{ t('蛋白文件') }} : {{ autoAnnotationConfirmData.protein_file }}
+
当前 gene2go : {{ autoAnnotationConfirmData.overlap_report.path }}
+
{{ t('交集') }} : {{ autoAnnotationConfirmData.overlap_report.normalized_overlap || 0 }} / {{ autoAnnotationConfirmData.overlap_report.de_genes || 0 }} DE genes
+
背景ID示例 : {{ (autoAnnotationConfirmData.overlap_report.background_examples || []).join(', ') }}
+
+
自动注释是长任务。40秒内不操作将自动确认;可暂停倒计时后手动决定。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 文献方法与 bbav5 阶段核对
+ {{ t('只会执行已勾选且可执行的阶段') }}
+
+
+
+
+
+
+ {{ stage.literature_stage }}
+ {{ stage.observation_id }}
+
+
{{ t('文献工具') }}
+
+
{{ t('未识别到文献工具') }}
+
+
+
{{ t('文献参数') }}
+
+ {{ param.parameter_name }} = {{ param.parameter_value }}
+
+
+
{{ stage.evidence_sentence }}
+
+
+ {{ note.parameter_name }} = {{ note.parameter_value }} · {{ note.note || note.status }}
+
+
+
+
+
+
+ {{ t('执行') }}
+ {{ stage.bbav5_stage_id || 'manual' }}
+ {{ stage.support_status }}
+
+
+ bbav5 工具
+
+
+
+
+
bbav5 参数
+
+
+
+ 输入要求: {{ JSON.stringify(stage.input_requirements) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
📋 待执行阶段列表 ({{ batchExecuteStages.length }}个)
+
+
+ {{ idx + 1 }}.
+
+ {{ typeof stage === 'object' ? (stage.name || getStageLabel(stage.id)) : getStageLabel(stage) }}
+
+ {{ t('已完成') }}
+ ✓
+ 执行中...
+
+
+
+
+
+
GWAS 物种与输出
+
+ 物种简称(用于结果前缀)
+
+ 本次完整 GWAS 的所有阶段统一使用该前缀。
+
+
+
+
+
+
+ 正在执行: {{ typeof batchExecuteStages[batchExecuteIndex] === 'object' ? (batchExecuteStages[batchExecuteIndex].name || getStageLabel(batchExecuteStages[batchExecuteIndex].id)) : getStageLabel(batchExecuteStages[batchExecuteIndex]) }} ({{ batchExecuteIndex + 1 }}/{{ batchExecuteStages.length }})
+
+
+
+ 参考数据下载:{{ referenceBundleDownloadState.speciesLabel || 'reference' }}
+ {{ referenceBundleDownloadState.status || 'running' }}
+
+
+
+
{{ referenceBundleDownloadState.message || '正在后台下载参考物种注释包' }}
+
+
+
+
+
数据角色(来自 config.xlsx)
+
+
+
Family
+
{{ batchExecuteParams.geneFamily || '未设置' }}
+
+
+
Scope
+
{{ scopeModeLabel(batchExecuteParams.scopeMode) }}
+
+
+
target 目标物种
+
{{ listText(batchExecuteParams.targetSpecies) }}
+
+
+
reference 参考物种{{ batchIncludesPpiKaks() ? '(仅作为初步推荐,执行前将确定技术参考物种)' : '' }}
+
{{ listText(batchExecuteParams.referenceSpecies, '无') }}
+
+
+
comparative 比较物种
+
{{ listText(batchExecuteParams.comparativeSpecies, '无') }}
+
+
+
+
+ Family
+
+
+
+
+ {{ t('仅目标') }}
+ 目标+参考
+ 目标+比较
+ {{ t('全部') }}
+
+
+
+
target 目标物种
+
+ {{ sp }}
+
+
+
+
{{ batchIncludesPpiKaks() ? 'reference 参考物种(仅作为初步推荐,执行前将确定技术参考物种)' : 'reference 参考物种(仅用于标签迁移)' }}
+
+ {{ sp }}
+
+
+
+
{{ t('种间共线性绘图设置') }}
+
正在加载种间物种列表...
+
+
Target species
+
+ {{ sp }}
+
+
Ref-side species
+
+ {{ sp }}
+
+
BLAST 引擎
+
+ {{ engine.label }}
+
+
{{ t('绘图模式') }}
+
+ {{ mode.label }}
+
+
默认生成所有物种总图和两两共线性图。
+
+
+
+ {{ outputLabelAutoMessage(getFirstGeneFamilyStage(batchExecuteStages), batchExecuteParams) }}
+
+
+
+
+
{{ t('多物种处理方式') }}
+
+
+
+
+ 多物种联合分析与确认(默认)
+ target、comparative 与 reference 放入同一棵树、同一张组合证据图和一张联合亚家族确认表;确认表只包含待分类物种。
+
+
+ {{ t('按单个物种分别分析与确认') }}
+ 每个待分类物种分别与 reference 生成独立树、组合图、亚家族分类结果和确认表。
+
+
+
+ 当前选择:{{ subfamilyAnalysisModeLabel(batchExecuteParams.analysisMode) }}。
+ {{ subfamilyStateText(batchExecuteParams) }}
+
+
+
+
+
本次亚家族分类对象(target + comparative)
+
+
选择本次分类/鉴定对象
+
+ {{ t('仅目标物种') }}
+ {{ t('仅比较物种') }}
+ 目标+比较物种
+
+
+
+
Target 角色物种(可作为本次待分析物种)
+
+
+ {{ item.species }}(未检测到蛋白FASTA)
+
+
+
+
+
Comparative 角色物种(可作为本次待分析物种)
+
+
+ {{ item.species }}(未检测到蛋白FASTA)
+
+
+
+
+
参考物种(仅参考,不参与目标鉴定)
+
+ {{ item.species }}
+
+
+
+ 本次亚家族分类对象:{{ listText(identifyExecutionSpecies(batchExecuteParams)) }}。comparative 可参与分类/鉴定,但不会改写为 target;reference 只作为亚家族标签迁移参考。
+
+
+
+
+
+
+ 请核对参数后手动确认执行
+
+
+ 批量执行进行中...
+
+ ❌ 取消
+ 🚀 确认并开始执行
+ 执行中...
+
+
+
+
+
+
+
+
+
{{ option.label }}
+
{{ option.description }}
+
+
+ {{ getStageLabel(stage) }}
+
+
+
+
+
+
+ {{ t('取消') }}
+
+
+
+
+
+
+
+
+
⚠️ 检测到问题
+
+
+ {{ diagnosisFixData.errorType }}
+
+
+ {{ diagnosisFixData.description }}
+
+
+
+
+
+
{{ diagnosisFixData.solutionTitle }}
+
+
+ {{ diagnosisFixData.reason }}
+
+
+
{{ t('诊断动作') }}
+
+
+ {{ action.policyLabel }}
+
+
+ {{ action.issue_code }}
+ : {{ action.action }}
+
+
+
+
+
+
{{ t('当前参数') }}
+
{{ diagnosisFixData.currentParamsDisplay }}
+
+
→
+
+
{{ t('建议参数') }}
+
{{ diagnosisFixData.suggestedParamsDisplay }}
+
+
+
+
+
+
+
+ {{ diagnosisFixData.footerNote }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Domain
+
+
+ max e-value/score
+
+
+ min coverage
+
+
+
+ 当前筛选命中 {{ domainReviewFilteredIds.length }} 个成员;默认保留 {{ domainReviewDefaultIds.length }} 个成员。
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.default_keep_bool ? 'keep' : 'review' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ subfamilyReviewFilteredRows.length }}/{{ subfamilyReviewRows.length }} kept / original {{ subfamilyReviewOriginalRowCount }}
+
+
+ 已修改 {{ subfamilyReviewChangedCount }} 个;已删除 {{ subfamilyReviewRemovedCount }} 个;置信度为 medium/low 的行会以浅黄色显示。
+
+
+
+
+ {{ subfamilyReviewDisplayIndex(row) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('删除') }}
+
+
+
+
+
+
+
+
+ 矩形树 + 亚家族 + Motif/Structure/Domain
+ {{ t('重置') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Adjust size and redraw
+
+
+
+ Width
+
+
+
+
+ Height
+
+
+
+
+ 🔄 Redraw
+
+
+
+
+
+
+
+
+
+ 当前显示 {{ tablePreviewFilteredRows.length }} / {{ tablePreviewRows.length }} 行
+
+
+
+
+ 未解析到表格行;请确认文件内容为 TSV/CSV
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('新增') }}
+
+
+
+
+
+ {{ t('保留') }}
+ gene_id
+ source
+ {{ t('操作') }}
+
+
+
+
+
+ {{ row.gene_id }}
+ {{ row.source }}
+ {{ t('删除') }}
+
+
+
+
+ 未加载到 gene ID;请确认 PPI 已生成输入基因列表
+
+
+
+ 当前保留 {{ ppiResetKeptCount }} / {{ ppiResetRows.length }} 个 ID
+
+
+
+ {{ t('取消') }}
+ 提交并重新运行 PPI
+
+
+
+
+
+
+
+
+
+ 📋 当前阶段: {{ getStageLabel(confirmParams.stage) }}
+
+
+ 进度: {{ stageConfirmCurrent }} / {{ stageConfirmTotal }}
+
+
+
+
+
+
{{ t('基本参数') }}
+
+
+
{{ isGwasStage(confirmParams.stage) ? '物种简称(用于结果前缀)' : '输出标签 / Prefix' }}
+
+
+ {{ outputLabelAutoMessage(confirmParams.stage) }}
+
+
+
+ 本阶段 CPU 线程数
+
+
+
+ {{ t('样本并行数量') }}
+
+ {{ t('同时处理的样本数') }}
+
+
+
+
+
+
GWAS 输入 VCF
+
+
+
+
+
+
+ 检测到多个有效 VCF,请选择
+
+
+
+
+
+
+
+
+
+
+
+
+ Java Xmx
+
+
+
+
+ {{ t('不受支持标记处理策略') }}
+
+
+
+
+ mask 会排除 TASSEL/HapMap 无法无损表示的标记;输出仅保留可严格复现的标记子集。
+
+
+
+
+
+
+
{{ t('群体遗传输入') }}
+
+
+ 11 样本 VCF(必填)
+
+
+
+ 样本分组表(PCA/LD 可选)
+
+
+
+
+
K 最小值
+
K 最大值
+
指定 GWAS K(0=仅报告最佳 K)
+
+
+
+ {{ t('染色体策略') }}
+
+
+
+
+
+
+
+ 染色体(逗号分隔)
+
+
+
+
+
+
+
+
PPI/KaKs 参考数据处理
+
+
+
+
+
+
+
{{ t('目标物种') }}
+
{{ confirmParams.ppiKaksTargetSpecies || '未识别' }}
+
+
+
{{ t('涉及阶段') }}
+
{{ (confirmParams.ppiKaksRequestedStages || []).map(getStageLabel).join('、') || 'PPI/KaKs' }}
+
+
+
{{ t('当前参考物种') }}
+
{{ confirmParams.ppiKaksCurrentReferenceSpecies || '未选择' }}
+
+
+
Ortholog 映射
+
{{ confirmParams.ppiKaksOrthologMapping ? '已检测到' : '未生成' }}
+
+
+
+
+
推荐近源/同源参考物种
+
+
+
+ {{ confirmParams.ppiKaksReferenceRecommendationName || confirmParams.ppiKaksReferenceRecommendationText }}
+ {{ confirmParams.ppiKaksReferenceRecommendationCode }}
+ STRING taxon {{ confirmParams.ppiKaksReferenceRecommendationTaxon }}
+
+
{{ confirmParams.ppiKaksReferenceRecommendationMeta || '来自 STRING 物种列表的近源参考推荐' }}
+
+
未找到可直接下载的 STRING 近源参考物种。
+
+
+
+
+
{{ t('数据需求') }}
+
+
+ 需要:
+ {{ item }}
+ pep
+
+
+ 当前缺失:
+ {{ item }}
+ {{ t('无') }}
+
+
+
+
+
+
{{ t('处理方式') }}
+
+
+ 下载/补齐推荐参考数据后继续
+ 直接使用项目参考物种 {{ confirmParams.ppiKaksProjectReferenceSpecies }}
+ 本次先跳过 PPI/KaKs
+
+ 系统会先准备参考 PEP/CDS,再由 Ortholog 生成映射,之后执行 PPI/KaKs。
+
+
+
+
+
+
+
PPI/KaKs 下载失败兜底确认
+
+
+
+
+
{{ t('原推荐参考') }}
+
+
+
+ {{ confirmParams.ppiKaksReferenceRecommendationName || confirmParams.ppiKaksReferenceRecommendationText }}
+ {{ confirmParams.ppiKaksReferenceRecommendationCode }}
+
+
{{ confirmParams.ppiKaksReferenceRecommendationMeta || '下载未完成,不能直接用于本次 PPI/KaKs。' }}
+
+
未记录推荐参考物种。
+
+
+
+
{{ t('下一步') }}
+
+
+ {{ t('使用拟南芥作为显式兜底') }}
+ 本次先跳过 PPI/KaKs
+
+ 拟南芥只会写入 Ortholog/PPI/KaKs 专用参考参数,不会把全流程其它分析阶段改成 At。
+
+
+
+
+
+
+
RNA-seq 分析链路确认
+
+
统计链: HISAT2 BAM -> featureCounts raw fragment counts -> DESeq2 -> DE genes -> GO/KEGG/FGSEA/WGCNA/candidate。
+
展示链: Salmon/tximport TPM 只在 bridge QC 通过后进入 expression plots;tximport counts_from_abundance 不作为默认 DESeq2 输入。
+
样本配置: {{ confirmParams.rnaseqSampleConfig || '自动使用 input/final 中的 samples.yaml' }}
+
+
+
+
+
RNA-seq QC 工具参数
+
+
+ QC 工具
+
+
+
+
+
+
+ fastp 可执行文件
+
+ 留空时使用 FASTP_BIN 或 PATH
+
+
+
+ 自动检测 PE adapter
+
+
+
+ cut front
+
+
+
+ cut tail
+
+
+
+ window size
+
+
+
+ mean quality
+
+
+
+ qualified phred
+
+
+
+ unqualified %
+
+
+
+ N base limit
+
+
+
+ length required
+
+
+
+
+
+
+
+
RNA-seq Align 参数
+
+
+ Known splice site 来源
+
+
+
+
+
+
+
+
+ splice site / 注释路径
+
+
+
+
+
+
+
RNA-seq 定量参数
+
+
+ {{ t('定量模式') }}
+
+
+
+
+
+
+ {{ t('丰度引擎') }}
+
+
+
+
+
+
+
+
+ Salmon libType
+
+
+
+ tximport counts
+
+
+
+
+
+
+
+
+ Transcriptome FASTA
+
+
+
+ tx2gene TSV
+
+
+
+ Kallisto bias
+
+
+
+
+ featureCounts raw fragment counts 是后续 DESeq2 的默认证据;Salmon/tximport TPM 用于表达展示,只有样本名和 gene ID overlap 检查通过后才会给 expression plots 使用。
+
+
+
+
+
RNA-seq 差异分析参数
+
+
+ padj 阈值
+
+
+
+ |log2FC| 阈值
+
+
+
+ {{ t('实验设计模块') }}
+
+
+
+
+
+
+
+
+
+
+ 设计审查: {{ confirmParams.rnaseqDesignReview.module }} - {{ confirmParams.rnaseqDesignReview.module_name }};{{ confirmParams.rnaseqDesignReview.design_formula }}
+ 批次: {{ confirmParams.rnaseqDesignReview.batch.display }}
+ • {{ item.display }}
+ • {{ confirmParams.rnaseqDesignReview.interaction.display }}(LRT + Wald)
+
+
{{ confirmParams.rnaseqDesignReview.error }}
+
+
+ DESeq2 默认输入为 featureCounts raw fragment counts;padj 与 |log2FC| 只影响显著 DE gene 筛选,不改变原始 count matrix。
+
+
+
+
+
RNA-seq 富集高级参数
+
+
+ pvalue cutoff
+
+
+
+ qvalue cutoff
+
+
+
+ {{ t('背景来源') }}
+
+
+
+
+
+
+
+
+ top pathways
+
+
+
+ OrgDb species
+
+
+
+ keyType
+
+
+
+ GO OBO
+
+
+
+ KEGG level
+
+
+
+
+
+
+ KEGG filter
+
+
+
+
+
+
+ Highlight mode
+
+
+
+
+
+
+
+ KEGG organism
+
+
+
+ Plot type
+
+
+
+
+
+
+ 禁用 KEGG 网络
+
+
+
+ 跳过 pathview
+
+
+
+
+
+
+
RNA-seq FGSEA 参数
+
+
+ comparison
+
+
+
+ min size
+
+
+
+ max size
+
+
+
+ permutations
+
+
+
+ seed
+
+
+
+ KEGG organism
+
+
+
+
+ FGSEA 使用 DESeq2 统计量排序,并使用 gene2ko/KEGG gene set;comparison 留空时自动选择可用比较。
+
+
+
+
+
RNA-seq WGCNA 参数
+
+
+ {{ t('表达矩阵') }}
+
+
+
+ count matrix
+
+
+
+ sample table
+
+
+
+ min mean
+
+
+
+ max genes
+
+
+
+ min module size
+
+
+
+ merge cut height
+
+
+
+ powers
+
+
+
+
+
+
+
RNA-seq Candidate 参数
+
+
+ max DE proteins
+
+
+
+ InterProScan
+
+
+
+
+
+
+
+ InterProScan applications
+
+
+
+
+
+
+
+
+
+
+ InterProScan 需要本机可执行文件和 Java 环境;auto 模式只在缺少可用功能注释且环境可用时运行。
+
+
+
+
+
{{ confirmParams.stage === 'identify' ? '本阶段成员鉴定对象' : '本阶段执行范围' }}
+
+
+
Family
+
{{ confirmParams.geneFamily || '未设置' }}
+
+
+
Scope
+
{{ scopeModeLabel(confirmParams.scopeMode) }}
+
+
+
{{ confirmParams.stage === 'identify' ? '将鉴定物种' : 'target 目标物种' }}
+
{{ listText(confirmParams.stage === 'identify' ? identifyExecutionSpecies(confirmParams) : confirmParams.targetSpecies) }}
+
+
+
reference 参考物种
+
{{ listText(confirmParams.referenceSpecies, '无') }}
+
+
+
{{ t('后续主参考物种') }}
+
{{ listText(confirmParams.pipelinePrimaryReferenceSpecies, '未设置') }}
+
+
+
+ Identify 只负责成员鉴定;这里显示的是本阶段执行集合,不改变 config 中 target / comparative / reference 的数据角色。
+ Identify BLAST参考:{{ listText(confirmParams.pipelineIdentifyBlastReferenceSpecies, '未设置') }}。
+ 后续主参考:{{ listText(confirmParams.pipelinePrimaryReferenceSpecies, '未设置') }}。
+
+
+
+ {{ t('仅目标') }}
+ 目标+参考
+ 目标+比较
+ {{ t('全部') }}
+
+
+
+
+ Family
+
+
+
+
+
+ {{ t('主参考') }}
+
+
+
+ BLAST参考
+
+
+
+
target 目标物种
+
+ {{ sp }}
+
+
+
+
reference 参考物种
+
+ {{ sp }}
+
+
+
+
comparative 比较物种
+
+ {{ sp }}
+
+
+
+
+
+
{{ confirmParams.stage === 'identify' ? '本阶段成员鉴定对象' : '本阶段执行范围' }}
+
+
+
Family
+
{{ confirmParams.geneFamily || "未设置" }}
+
+
+
Scope
+
{{ scopeModeLabel(confirmParams.scopeMode) }}
+
+
+
{{ confirmParams.stage === 'identify' ? '将鉴定物种' : 'target 目标物种' }}
+
{{ listText(confirmParams.stage === 'identify' ? identifyExecutionSpecies(confirmParams) : confirmParams.targetSpecies) }}
+
+
+
reference 参考物种
+
{{ listText(confirmParams.referenceSpecies, "无") }}
+
+
+
{{ t('后续主参考物种') }}
+
{{ listText(confirmParams.pipelinePrimaryReferenceSpecies, "未设置") }}
+
+
+
Identify BLAST参考
+
{{ listText(confirmParams.pipelineIdentifyBlastReferenceSpecies, "未设置") }}
+
+
+
comparative 比较物种
+
{{ listText(confirmParams.comparativeSpecies, "无") }}
+
+
+
+ Identify 只负责成员鉴定;这里显示的是本阶段执行集合,不改变 config 中 target / comparative / reference 的数据角色。
+ Identify BLAST参考:{{ listText(confirmParams.pipelineIdentifyBlastReferenceSpecies, "未设置") }}。
+ 后续主参考:{{ listText(confirmParams.pipelinePrimaryReferenceSpecies, "未设置") }}。
+
+
+
+
+
+
+
+
+
+
+
+
+
Identify 参数
+
+
{{ t('本次成员鉴定物种') }}
+
+
目标物种(默认勾选)
+
+
+ {{ item.species }}(未检测到蛋白FASTA)
+
+
+
+
+
比较物种(按需勾选)
+
+
+ {{ item.species }}(未检测到蛋白FASTA)
+
+
+
+
+
参考物种(仅参考,不参与目标鉴定)
+
+ {{ item.species }}
+
+
+
+ 当前鉴定:{{ listText(identifyExecutionSpecies(confirmParams)) }}
+
+
+
+
+
+ hmmsearch domain i-Evalue
+
+
+
+
+ HMM Coverage 0-1
+
+
+
+
+
+
+ Blast E-value
+
+
+
+
+ Blast Identity %
+
+
+
+
+
+
+ Required Pfams
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Identify 需要 HMM 文件或 Required Pfams 二选一。WOX 家族常见为 PF07507 ;也可在 config.xlsx 的 Analysis.Required_Pfams 预先填写。
+
+
+
+
+
Phylogeny 参数
+
+
+
+ MAFFT
+
+
+
+
+
+
+ IQ-TREE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Motif 参数
+
+
+
+ Motif数量
+
+
+
+
+
+
+ {{ t('最小宽度') }}
+
+
+
+
+ {{ t('最大宽度') }}
+
+
+
+
+
+
+
Domain 参数
+
+
+
+ hmmscan E-value
+
+
+
+
+ Coverage %
+
+
+
+
+
+
+
TFBS 参数
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('基因家族 promoter 扫描') }}
+ 同时跑全注释基因 promoter 背景
+
+
+
+
+
+
+
+
+
+ {{ t('扫描双链') }}
+
+
+
+
+
{{ t('亚家族确认参数') }}
+
+
+
+ {{ t('参考分类') }}
+
+
+
+
+
+
+ {{ t('目标前缀') }}
+
+
+
+
+ {{ t('参考前缀') }}
+
+
+
+
+
+ 覆盖 final/{{ confirmParams.prefix || 'prefix' }}.geneID.subfamily 兼容文件
+
+
+
+
+
+
{{ t('种间共线性比较物种') }}
+
正在从 config.xlsx 加载物种列表...
+
+
+ 选择 target 侧与 ref 侧比较物种;默认勾选 config 中全部 ref-side 物种。
+
+
+
Target species
+
+ {{ sp }}
+
+
未找到 target 物种
+
+
+
Reference / comparative species
+
+ {{ sp }}
+
+
未找到 reference 物种
+
+
+
BLAST 引擎
+
+ {{ engine.label }}
+
+
+ diamond 为默认快速引擎;blastp 使用 NCBI BLAST+。两者均输出 MCScanX 所需的 outfmt 6。
+
+
+
+
{{ t('绘图模式') }}
+
+ {{ mode.label }}
+
+
+
+
{{ t('当前选择') }} :targets {{ listText(confirmParams.targetSpecies) }};ref-side {{ listText(confirmParams.refSpeciesList) }};BLAST 引擎 {{ confirmParams.blastEngine || 'diamond' }};绘图模式 {{ confirmParams.interspeciesPlotMode || 'both' }}。
+
+
+
+
+
+
+
PPI 参数
+
+
+
+ {{ t('参考物种') }}
+
+
+
+
+ Score阈值
+
+
+
+
+ 💡 Gene IDs are read from final/{{ (confirmParams.prefix || '').trim() || 'At' }}.final_members.id
+
+
+
+
+
+
KaKs 参数
+
+
+
+ {{ t('中性速率') }}
+
+
+
+
+
+
+
+
Expression 参数 (FPKM/TPM矩阵)
+
+
+
+ GEO编号
+
+
+
+
+
+
+
+
+
+
+
+
+
+
📊 表达量数据来源(Select一种):
+
+
✅ 方式1: 直接上传 - 上传表达量矩阵(*.fpkm.txt/*.tpm.txt)+基因ID+sample.txt到Input目录
+
✅ 方式2: GEO编号 - InputGEO编号(如 GSE111937)自动Download
+
+
+
+
+
+
+
RNA-seq 参数
+
+
+
+ {{ t('拉丁名') }}
+
+
+
+
+ KEGG organism
+
+
+
+RNA-seq KEGG/fgsea 会优先使用物种级 KEGG gene set;没有物种级缓存时再回退到 KO 全库植物过滤。当前默认值来自 samples.yaml 元数据、job 资源前缀或已完成 manifest。
+
+
+
+
+
+ {{ t('差异表达范围') }}
+
+
+
+
+
+
+ 当前阶段会复用 RNA-seq 定量输出和 samples.yaml。若选择基因家族差异表达,需要已有 final_members.id;若选择全基因组差异表达,不依赖基因家族成员列表。
+
+
+
+
+
+ {{ t('富集类型') }}
+
+
+
+
+
+
+
+ {{ t('前景基因') }}
+
+
+
+
+
+
+
+ {{ t('背景来源') }}
+
+
+
+
+
+
+
+
+ Applications
+
+
+
+
+
+
+
+
+
+ OrgDb物种
+
+
+
+
+
+
+
+
+
+
+ KEGG organism
+
+
+
+ 自动注释会按当前富集类型补缺失背景:GO 缺 gene2go 时优先用 AnnotationHub/biomaRt,失败再回退 InterProScan;KEGG 缺 gene2ko 时仍运行 KofamScan。
+
+
+
+ 当前阶段会复用 samples.yaml 和上游真实产物;如缺少输入,后端预检/日志会给出具体缺失文件。
+
+
+
+
+
+
Combo 修订参数
+
+
当前图片:{{ getFileName(confirmParams.comboSourceImage) }}
+
选择要在 combo 图中展示的分析类型;单类型图请使用对应阶段已有结果。
+
+
+
+ Motif
+ Gene structure
+ Domain
+
+
+
+ 至少选择两个类型;默认三类全选。重绘会复用已完成的 phylogeny/motif/structure/domain 结果,不重新运行上游分析。
+
+
+
+
+
{{ t('启动子自定义绘图') }}
+
+
复用已完成 promoter 阶段的 PlantCARE filtered 表,不重新提交 PlantCARE。
+
+
+
+ {{ t('输出标签') }}
+
+
+
+
{{ t('启动子元件') }}
+
+
+
+
+
+
{{ t('功能类别') }}
+
+ {{ item.category }} ({{ item.count }})
+
+
+
+
+
{{ t('匹配方式') }}
+
+
+
+
+
+
+
+
{{ t('图例颜色') }}
+
+
+
+
+
+
+
+
+
+
{{ t('图类型') }}
+
+ {{ t('位置分布图') }}
+ Heatmap
+ Barplot
+
+
+
+
+
+
种内共线性 Circos 修订参数
+
+
当前图片:{{ getFileName(confirmParams.syntenySourceImage) }}
+
绘图数据:复用 synteny 阶段的 GFF、MCScanX collinearity、family gene ID 和 genome。
+
+
+
+ {{ t('输出标签') }}
+
+
+
+
+
+
+ 基因ID距离参数对应 Circos text plot 的 r0;数值越小越靠近染色体色块。字号增大时系统会自动加宽外侧文字轨道,避免 Circos 丢弃标签。默认不覆盖原图,生成 synteny_replot_circos 文件。
+
+
+
+
+
+
{{ t('进化树图修订参数') }}
+
+
当前图片:{{ getFileName(confirmParams.treeSourceImage) }}
+
树文件:{{ confirmParams.treeFile }}
+
树文件:将根据当前图片自动解析对应 .tree.nwk
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('输出标签') }}
+
+
+
+
{{ t('标记模式') }}
+
+
+
+
+
+
+
+
+
+
+
{{ t('树枝颜色') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
参考物种形状(角色树)
+
+
+
+
+
+
+
+
+
+
+
+ 输出将使用 tree_replot 命名,不覆盖原始树图。标记模式为“自动”时,多物种树按物种生成 Species 图例,单物种树使用统一 Tip 标记;角色树才会区分目标/参考物种形状。
+
+
+
+
+
MSA区间绘图参数
+
+
当前图片:{{ getFileName(confirmParams.msaSourceImage) }}
+
MSA来源:{{ confirmParams.msaFile }}
+
MSA来源:将根据当前图片/manifest自动解析
+
+
+
+ 推荐保守区:alignment {{ confirmParams.msaRecommendedStartCol }}-{{ confirmParams.msaRecommendedEndCol }}
+ score={{ Number(confirmParams.msaRecommendedScore || 0).toFixed(3) }}, non-gap={{ Number(confirmParams.msaRecommendedNongap || 0).toFixed(3) }}
+
+
{{ t('使用推荐区间') }}
+
+
+ 正在分析当前 MSA 的保守区推荐...
+
+
+
+ {{ t('参考基因编号') }}
+ alignment列
+ {{ t('自动保守窗口') }}
+
+
+
+
+
{{ t('参考基因') }}
+
+
+
+ {{ seq.id }}
+ {{ seq.header }}
+
+
+
+
+
+
+
+
+
+
+ 参考基因编号按所选基因的非 gap 残基计数;系统会自动换算为 alignment 列后截取所有序列。alignment 列模式包含缺口列;自动模式按保守度窗口筛选。
+
+
+
+
+ 💡 提示: 请先选择有效 VCF;当前不会自动执行。
+ 💡 提示: 下载失败后的拟南芥兜底必须手动确认,不会自动执行。
+ 💡 提示: 40秒内不操作将使用当前参数自动执行,可随时暂停倒计时后手动确认。
+
+
+
+
+
+
+
+
+
+
+
+
✓
+
+ {{ resultDialogParams.stage }} 执行完成
+
+
+ ⏱️ 执行时间: {{ resultDialogParams.elapsed }}
+
+
+
+ {{ t('确定') }}
+
+
+
+
+
+
+
+
Job ID
+
{{ t('已完成阶段') }}
+
{{ t('操作') }}
+
+
+
{{ job.job_id }}
+
+
+
+ {{ stage }}
+
+
+ {{ t('无') }}
+
+
+ Select
+
+
+
+
+ 暂无已有Working Directory
+
+
+ {{ t('取消') }}
+
+
+
+
+
+
+
+
+
+
+
📈 当前累计进展
+
+
+
+
{{ index === interpretationReport.stage_flow.stages.length - 1 ? '🎯' : '✅' }}
+
{{ stage.name }}
+
{{ stage.key_result }}
+
+ →
+
+
+
+
+
🔗 阶段间关联分析
+
+
+
{{ conn.description }}
+
+ 💡 {{ conn.insight }}
+
+
+
+
+
💡 当前累计发现
+
+
{{ finding.id }}. {{ finding.title }}
+
{{ finding.description }}
+
📋 证据: {{ finding.evidence }}
+
+
+
🧩 跨阶段 Results 解读
+
+
{{ item.title || item.group }} · {{ item.readiness }}
+
{{ item.result_interpretation }}
+
阶段: {{ item.stages.join('、') }}
+
缺口: {{ item.missing_slots.join(';') }}
+
边界: {{ item.boundary }}
+
+
+
三个 agent 贡献审计
+
+
+
结果 agent
+
+ 阶段 {{ interpretationReport.agent_contribution_audit.result_agent?.stage_count || 0 }} 个;
+ 事实证据 {{ interpretationReport.agent_contribution_audit.result_agent?.fact_count || 0 }} 条;
+ 风险 {{ interpretationReport.agent_contribution_audit.result_agent?.risk_count || 0 }} 条
+
+
{{ interpretationReport.agent_contribution_audit.result_agent?.boundary }}
+
+
+
发现 agent
+
+ claims {{ interpretationReport.agent_contribution_audit.discovery_agent?.claim_count || 0 }} 条;
+ 假设 {{ interpretationReport.agent_contribution_audit.discovery_agent?.hypothesis_count || 0 }} 条;
+ 低置信 {{ interpretationReport.agent_contribution_audit.discovery_agent?.low_confidence_claim_count || 0 }} 条
+
+
{{ interpretationReport.agent_contribution_audit.discovery_agent?.boundary }}
+
+
+
RAG agent
+
+ 来源 {{ interpretationReport.agent_contribution_audit.rag_agent?.reference_count || 0 }} 条;
+ 直接支持 {{ interpretationReport.agent_contribution_audit.rag_agent?.claim_bound_count || 0 }};
+ 方法/背景 {{ interpretationReport.agent_contribution_audit.rag_agent?.method_support_count || 0 }}/{{ interpretationReport.agent_contribution_audit.rag_agent?.biological_background_count || 0 }}
+
+
{{ interpretationReport.agent_contribution_audit.rag_agent?.boundary }}
+
+
+
+
🔎 科学发现 / 候选假设
+
+
{{ item.id }}. {{ item.title }}
+
{{ item.claim }}
+
证据编号: {{ item.evidence_refs.join('、') }}
+
📋 证据: {{ item.evidence }}
+
解读: {{ item.interpretation }}
+
可信度: {{ item.confidence }};边界: {{ item.caveat }}
+
RAG解读: {{ interpretationRagStatusLabel(item.rag_binding_status) }}
+
可借鉴写法: {{ item.rag_interpretation.suggested_wording }}
+
+
+
+ 🧪 证据质量 / RAG解读审计
+
+ 刷新RAG解读
+
+
+
+
+
{{ interpretationReport.source_summary?.claim_count || 0 }}
+
Claims
+
+
+
{{ interpretationReport.source_summary?.raw_evidence_count || 0 }}
+
{{ t('原始证据') }}
+
+
+
{{ interpretationReport.rag_audit.reference_count || 0 }}
+
RAG来源
+
+
+
{{ interpretationReport.rag_audit.claim_binding_count || 0 }}
+
文献支撑claim
+
+
+
{{ interpretationReport.rag_audit.method_reference_count || 0 }}
+
{{ t('方法参考') }}
+
+
+
{{ interpretationReport.rag_audit.biological_context_count || 0 }}
+
{{ t('背景知识') }}
+
+
+
{{ interpretationReport.rag_audit.status || 'not_available' }}
+
{{ interpretationReport.rag_audit.message }}
+
+
+
+
🧬 Claim级结果解读
+
+
+ {{ claim.claim_id }}
+ {{ interpretationRagStatusLabel(claim.rag_binding_status) }}
+
+
{{ claim.title }}
+
{{ claim.claim }}
+
+ 解读: {{ claim.interpretation }}
+
+
+ 边界: {{ claim.caveat }}
+
+
+ 本地证据 {{ (claim.local_evidence || []).length }} 条;反证/风险 {{ (claim.counter_evidence || []).length }} 条;可信度 {{ claim.confidence || 'medium' }}
+
+
+ 证据编号: {{ claim.evidence_refs.join('、') }}
+
+
+ 可信度依据: {{ claim.confidence_reason }}
+
+
+
+ {{ ev.stage || 'evidence' }}
+ {{ ev.statement }}
+
+
+
+
RAG解读增强
+
+ 文献支撑当前结论: {{ interpretationReferenceList(claim.rag_interpretation.claim_support) }}
+
+
+ 方法学参考: {{ interpretationReferenceList(claim.rag_interpretation.method_reference) }}
+
+
+ 生物学背景: {{ interpretationReferenceList(claim.rag_interpretation.biological_context) }}
+
+
+ 边界提醒: {{ claim.rag_interpretation.boundary_note }}
+
+
+ 可借鉴写法: {{ claim.rag_interpretation.suggested_wording }}
+
+
+ 文献启发的下一步: {{ claim.rag_interpretation.next_validation_from_literature.slice(0, 2).join(';') }}
+
+
+
+ 未作为当前结论支撑的原因: {{ claim.rag_rejected_reason }}
+
+
+ 下一步验证: {{ claim.next_validation.slice(0, 2).join(';') }}
+
+
+
+
⚠️ 解释风险
+
+
+
🧾 阶段审阅
+
+
{{ review.stage_name }}
+
{{ (review.key_points || []).join(';') }}
+
可发表性: {{ review.publication_readiness }}
+
+
+
+
🧬 生物学故事
+
+ {{ interpretationReport.biological_story }}
+
+
+
+
📋 下一步建议
+
+
+ {{ step.priority === 'high' ? '高优先级' : '中优先级' }}
+
+
+
{{ step.stage }}: {{ step.description }}
+
{{ step.rationale }}
+
+
+
+
🧷 证据卡
+
+
+
+ {{ card.id || ('E' + String(idx + 1).padStart(3, '0')) }} · {{ card.stage || 'unknown' }}
+ {{ card.source_type || card.interpretation_level || 'evidence' }}
+
+
{{ card.claim }}
+
{{ card.evidence }}
+
{{ card.source_file }}
+
+
+
+
📚 RAG参考来源
+
+
+
+ {{ ref.claim_id || ref.stage || 'RAG' }}
+ {{ ref.support_level || ref.status || ref.source_type || 'reference' }}
+
+
{{ ref.title || ref.url }}
+
{{ ref.title || ref.paper_title || ref.message || ref.query || '参考来源' }}
+
{{ [ref.source, ref.paper_id, ref.journal, ref.year].filter(Boolean).join(' · ') }}
+
{{ ref.evidence || ref.excerpt || ref.snippet || ref.answer_preview }}
+
Query: {{ ref.query }}
+
+
+
+
+
+
+
+ 📥 下载文本报告
+
+
+ 关闭
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ step.icon }}
+ {{ step.name }}
+ {{ step.time }}ms
+
+
+
+
+
+
📝 {{ t('Self-RAG反思') }}
+
+
{{ t('迭代') }}: {{ reflection.iteration }}/{{ reflection.maxIterations }}
+
{{ t('原因') }}: {{ reflection.reason }}
+
+ {{ t('重写查询') }}: {{ reflection.rewrittenQuery }}
+
+
+
+
+
+
+
🔀 MultiHopRAG
+
+
{{ t('跳数') }}: {{ multiHop.hops }}
+
{{ t('来源') }}: {{ multiHop.sources.join(', ') }}
+
+
+
+
+
+
+
+
+
+ {{ t('节点总数') }}: {{ kgStats.nodes }} | {{ t('关系总数') }}: {{ kgStats.edges }}
+
+
+
+
+
+
📄 {{ t('检索结果') }}
+
+
+
{{ result.content }}
+
+
+
+
+
+
+
{{ getRoleLabel(msg.role) }}
+
+
+ 📚 {{ t('参考文献') }}
+
+
+
{{ formatTime(msg.timestamp) }}
+
+
+
+
+
+
+ {{ isProcessing ? t('处理中...') : t('发送') }}
+
+
+
+
+
📋 {{ t('任务列表') }}
+
+ {{ getStatusIcon(task.status) }}
+ {{ task.message }}
+ {{ task.progress }}%
+
+
+
+
+
+
+
+
+
+
+
+
+