Yuanclaw / modules /ui_theme.R
huashu's picture
Export YuanSeq to Hugging Face without binary assets
7e6a9d1
# =====================================================
# 🍎 Apple-style UI + Dark Mode
# 完整 UI 模块(完全替换你之前的 sci_fi UI)
# =====================================================
library(shiny)
library(shinyWidgets)
library(colourpicker)
library(shinyjs)
library(bslib)
# =====================================================
# 🍎 Apple 风格主题样式(完整可运行版)
# =====================================================
sci_fi_css <- tags$style(HTML("
/* 基础字体 - 添加微妙渐变背景 */
body {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #F5F5F7 0%, #EFEFF4 50%, #F5F5F7 100%); /* 🔥 添加微妙渐变 */
margin: 0;
padding: 0;
font-size: 14px;
min-height: 100vh; /* 🔥 确保背景覆盖整个视口 */
}
/* 响应式字体大小 */
@media (max-width: 1200px) {
body {
font-size: 13px;
}
}
@media (max-width: 768px) {
body {
font-size: 12px;
}
}
@media (max-width: 480px) {
body {
font-size: 11px;
}
}
/* 开发者页脚链接 */
.xseq-developer-footer a {
color: #0d6efd;
text-decoration: none;
}
.xseq-developer-footer a:hover {
text-decoration: underline;
}
.dark-mode .xseq-developer-footer a {
color: #6ea8fe;
}
/* 夜间模式 - 添加微妙渐变背景 */
.dark-mode {
background: linear-gradient(135deg, #1C1C1E 0%, #252528 50%, #1C1C1E 100%) !important; /* 🔥 深色模式渐变 */
color: #FFFFFF !important;
min-height: 100vh;
}
/* 夜间模式下的所有元素 */
.dark-mode,
.dark-mode body,
.dark-mode .navbar,
.dark-mode .sidebar-panel,
.dark-mode .main-panel,
.dark-mode .well-panel,
.dark-mode .tab-pane,
.dark-mode .shiny-html-output,
.dark-mode .container-fluid,
.dark-mode .shiny-text-output,
.dark-mode .form-group,
.dark-mode .control-label,
.dark-mode .help-block,
.dark-mode .text-muted,
.dark-mode .radio,
.dark-mode .checkbox,
.dark-mode .well,
.dark-mode .panel,
.dark-mode .panel-body {
color: #FFFFFF !important;
background-color: #1C1C1E !important;
}
/* 所有文本元素强制白色 */
.dark-mode h1,
.dark-mode h2,
.dark-mode h3,
.dark-mode h4,
.dark-mode h5,
.dark-mode h6,
.dark-mode p,
.dark-mode span,
.dark-mode div,
.dark-mode label {
color: #FFFFFF !important;
}
/* 文件上传组件 */
.dark-mode .shiny-input-container,
.dark-mode .form-group,
.dark-mode .control-label {
color: #FFFFFF !important;
}
.dark-mode .shiny-file-input-label,
.dark-mode .input-group-addon {
color: #FFFFFF !important;
background-color: #2C2C2E !important;
border-color: #444444 !important;
}
/* 按钮组 */
.dark-mode .btn-group,
.dark-mode .btn-group .btn {
color: #FFFFFF !important;
}
/* 下载按钮 */
.dark-mode .download-button,
.dark-mode .shiny-download-link {
color: #FFFFFF !important;
}
/* 文件浏览区域 */
.dark-mode .file-input,
.dark-mode .shiny-bound-input {
color: #FFFFFF !important;
}
/* 进度条 */
.dark-mode .progress,
.dark-mode .progress-bar {
background-color: #2C2C2E !important;
color: #FFFFFF !important;
}
/* Shiny特定组件 */
.dark-mode .shiny-bound-output,
.dark-mode .shiny-html-output,
.dark-mode .shiny-text-output,
.dark-mode .shiny-plot-output,
.dark-mode .shiny-table-output {
color: #FFFFFF !important;
background-color: #1C1C1E !important;
}
/* 文件上传进度 */
.dark-mode .shiny-file-input-progress,
.dark-mode .progress-text {
color: #FFFFFF !important;
}
/* 文件上传按钮 */
.dark-mode .btn-file {
color: #FFFFFF !important;
background-color: #2C2C2E !important;
border-color: #444444 !important;
}
/* 文件上传区域 */
.dark-mode .file-input-wrapper,
.dark-mode .file-input-name {
color: #FFFFFF !important;
}
/* 下载链接 */
.dark-mode a[download],
.dark-mode .shiny-download-link a {
color: #0A84FF !important;
}
/* 输入框特殊处理 */
.dark-mode .form-control,
.dark-mode .selectize-input {
color: #000000 !important;
background-color: #FFFFFF !important;
}
.dark-mode select {
color: #000000 !important;
background-color: #FFFFFF !important;
}
/* 下拉菜单选项样式 */
.dark-mode select option {
color: #000000 !important;
background-color: #FFFFFF !important;
}
/* Selectize下拉菜单选项样式 */
.dark-mode .selectize-dropdown {
background-color: #FFFFFF !important;
border: 1px solid #444444 !important;
}
.dark-mode .selectize-dropdown .option {
color: #000000 !important;
background-color: #FFFFFF !important;
}
.dark-mode .selectize-dropdown .option:hover {
background-color: #F0F0F0 !important;
color: #000000 !important;
}
.dark-mode .selectize-dropdown .active {
background-color: #007AFF !important;
color: #FFFFFF !important;
}
/* 按钮 */
.dark-mode .btn {
color: #FFFFFF !important;
}
/* 表格 */
.dark-mode table,
.dark-mode th,
.dark-mode td {
color: #FFFFFF !important;
background-color: #2C2C2E !important;
}
/* 标签页 */
.dark-mode .nav-tabs .nav-link {
color: #CCCCCC !important;
}
.dark-mode .nav-tabs .nav-link.active {
color: #0A84FF !important;
}
.dark-mode .nav-tabs .nav-link:hover {
color: #FFFFFF !important;
}
a { color: #007AFF; }
.dark-mode a { color: #0A84FF; }
/* 导航栏 - 简化效果提升速度 */
.navbar {
background-color: rgba(255,255,255,0.95) !important;
border-bottom: 1px solid rgba(0,0,0,0.05);
}
.dark-mode .navbar {
background-color: rgba(28,28,30,0.95) !important;
border-bottom: 1px solid rgba(255,255,255,0.05);
}
.navbar-brand, .nav-link {
color: #111 !important;
font-weight: 500;
}
.dark-mode .navbar-brand, .dark-mode .nav-link {
color: #F2F2F7 !important;
}
/* 面板 - 增强阴影效果提升视觉层次 */
.sidebar-panel, .main-panel, .well-panel, .tab-pane {
background-color: #FFFFFF !important;
border-radius: 14px;
padding: 20px;
border: 1px solid rgba(0,0,0,0.06);
box-shadow: 0 4px 16px rgba(0,0,0,0.08); /* 🔥 增强阴影:更明显更有层次 */
transition: box-shadow 0.3s ease; /* 🔥 添加柔和过渡效果 */
}
/* 面板hover效果 */
.sidebar-panel:hover, .main-panel:hover, .well-panel:hover {
box-shadow: 0 6px 24px rgba(0,0,0,0.12); /* 🔥 hover时阴影更深 */
}
.dark-mode .sidebar-panel,
.dark-mode .main-panel,
.dark-mode .well-panel,
.dark-mode .tab-pane {
background-color: #2C2C2E !important;
border: 1px solid rgba(255,255,255,0.08);
box-shadow: 0 4px 16px rgba(0,0,0,0.5); /* 🔥 增强深色模式阴影 */
transition: box-shadow 0.3s ease;
}
.dark-mode .sidebar-panel:hover,
.dark-mode .main-panel:hover,
.dark-mode .well-panel:hover {
box-shadow: 0 6px 24px rgba(0,0,0,0.7); /* 🔥 深色模式hover效果 */
}
/* 按钮 */
.btn {
border-radius: 10px !important;
padding: 8px 14px !important;
font-weight: 500 !important;
border: none !important;
font-size: 14px !important;
}
/* 响应式按钮字体大小 */
@media (max-width: 1200px) {
.btn {
font-size: 13px !important;
padding: 7px 12px !important;
}
}
@media (max-width: 768px) {
.btn {
font-size: 12px !important;
padding: 6px 10px !important;
}
}
@media (max-width: 480px) {
.btn {
font-size: 11px !important;
padding: 5px 8px !important;
}
}
.btn-primary { background-color: #007AFF !important; color: white !important; }
.dark-mode .btn-primary { background-color: #0A84FF !important; }
.btn-success { background-color: #34C759 !important; color: white !important; }
.btn-warning { background-color: #FF9F0A !important; }
.btn-info { background-color: #5AC8FA !important; }
/* 🔥 增强按钮hover效果 - 添加transform和阴影 */
.btn:hover {
transform: translateY(-2px); /* 🔥 向上浮动 */
box-shadow: 0 4px 12px rgba(0,122,255,0.3); /* 🔥 添加阴影 */
opacity: 1; /* 🔥 不再降低opacity */
}
.btn-primary:hover {
box-shadow: 0 4px 12px rgba(0,122,255,0.4);
}
.btn-success:hover {
box-shadow: 0 4px 12px rgba(52,199,89,0.4);
}
.btn-warning:hover {
box-shadow: 0 4px 12px rgba(255,159,10,0.4);
}
.btn-info:hover {
box-shadow: 0 4px 12px rgba(90,200,250,0.4);
}
/* 深色模式按钮hover */
.dark-mode .btn:hover {
box-shadow: 0 4px 12px rgba(10,132,255,0.5);
}
.dark-mode .btn-primary:hover {
box-shadow: 0 4px 12px rgba(10,132,255,0.6);
}
/* 输入框 - 移除过渡效果提升速度 */
.form-control, .selectize-input {
background-color: #FAFAFC !important;
border-radius: 10px !important;
border: 1px solid rgba(0,0,0,0.08) !important;
height: 38px;
}
/* 下拉菜单选项样式 */
select option {
color: #000000 !important;
background-color: #FFFFFF !important;
}
/* Selectize下拉菜单选项样式 */
.selectize-dropdown {
background-color: #FFFFFF !important;
border: 1px solid rgba(0,0,0,0.1) !important;
}
.selectize-dropdown .option {
color: #000000 !important;
background-color: #FFFFFF !important;
}
.selectize-dropdown .option:hover {
background-color: #F0F0F0 !important;
color: #000000 !important;
}
.selectize-dropdown .active {
background-color: #007AFF !important;
color: #FFFFFF !important;
}
.form-control:focus, .selectize-input.focus {
border-color: #007AFF !important;
box-shadow: 0 0 0 3px rgba(0,122,255,0.15);
}
.dark-mode .form-control,
.dark-mode .selectize-input {
background-color: #3A3A3C !important;
border: 1px solid rgba(255,255,255,0.1) !important;
color: #F2F2F7 !important;
}
.dark-mode .form-control:focus {
border-color: #0A84FF !important;
box-shadow: 0 0 0 3px rgba(10,132,255,0.2);
}
/* 标签页 */
.nav-tabs .nav-link {
border: none !important;
padding: 10px 16px !important;
color: #6E6E73;
}
.nav-tabs .nav-link.active {
color: #007AFF !important;
border-bottom: 3px solid #007AFF !important;
background: transparent !important;
font-weight: 600;
}
.dark-mode .nav-tabs .nav-link.active {
color: #0A84FF !important;
border-bottom: 3px solid #0A84FF !important;
}
/* DataTable */
.dataTables_wrapper { color: #111 !important; }
.dark-mode .dataTables_wrapper { color: #F2F2F7 !important; }
"))
# =====================================================
# JS 控制:主题切换(自动加 body.dark-mode)
# =====================================================
theme_js <- tags$script(HTML("
Shiny.addCustomMessageHandler('toggle-darkmode', function(is_dark){
if(is_dark){
document.body.classList.add('dark-mode');
} else {
document.body.classList.remove('dark-mode');
}
});
"))
# 移除了Font Awesome依赖,使用emoji替代
# =====================================================
# 欢迎页
# =====================================================
landing_ui <- tagList(
# font_awesome, # 已移除Font Awesome依赖,使用emoji替代
wellPanel(
id = "landing_panel",
# Logo和标题显示
tags$div(class="text-center", style="margin-bottom: 20px; min-height: 300px; display: flex; flex-direction: column; align-items: center; justify-content: center;",
# Logo显示
tags$img(src="logo.png",
alt="YuanSeq",
style="max-height: 300px; width: auto; object-fit: contain; margin-bottom: 20px;",
class="img-responsive",
id="main-logo"
),
# 文字标题(logo加载失败时显示)
tags$h1(id="text-title",
class="text-center",
style="font-size: 28px; margin: 0; display: none;",
span(style="color: #007AFF;", "YuanSeq"),
span(style="color: #6E6E73; font-weight: normal;", " v1")
),
# JavaScript处理logo加载和论文展开/折叠
tags$script(HTML("
document.addEventListener('DOMContentLoaded', function() {
var img = document.getElementById('main-logo');
var textTitle = document.getElementById('text-title');
if (img && textTitle) {
// 图片加载成功时隐藏文字标题
img.onload = function() {
textTitle.style.display = 'none';
img.style.display = 'block';
};
// 图片加载失败时显示文字标题
img.onerror = function() {
img.style.display = 'none';
textTitle.style.display = 'block';
};
}
});
// 🔥 论文展开/折叠函数
function toggleOldPapers() {
var oldPapers = document.getElementById('old-papers');
var btn = document.getElementById('toggle-papers');
if (oldPapers.style.display === 'none') {
oldPapers.style.display = 'block';
btn.innerHTML = '📕 收起早期论文 ▲';
} else {
oldPapers.style.display = 'none';
btn.innerHTML = '📖 查看早期论文 ▼';
}
}
"))
),
# YuanSeq 简介
tags$div(class="text-center", style="margin-bottom: 35px; color: #6E6E73;",
tags$p("R/Shiny 转录组与芯片数据分析平台", style="font-size: 18px; margin-bottom: 12px;"),
tags$p("差异表达、KEGG/GO/GSEA 富集、通路与转录因子活性推断、交互可视化", style="font-size: 16px; margin-bottom: 0;")
),
tags$hr(style="margin: 30px 0;"),
# 项目信息
tags$div(style="margin-bottom: 35px;",
tags$h4("ℹ️ 项目信息", style="color: #007AFF; margin-bottom: 20px; font-size: 22px;"),
tags$p("• 版本:1", style="margin-bottom: 12px; font-size: 18px;"),
tags$p("• 更新日期:2025年12月26日", style="margin-bottom: 0; font-size: 18px;")
),
tags$hr(style="margin: 30px 0;"),
# 登录移除说明
tags$div(class="text-center", style="margin: 10px 0 30px; color: #6E6E73;",
tags$p("已移除登录与用户管理功能,直接进入分析工作台。", style="font-size: 16px; margin-bottom: 0;")
),
# 开发者简介已移除(隐私与精简考虑)
tags$div(style="margin-bottom: 10px;")
)
)
# =====================================================
# 主界面 UI
# =====================================================
main_app_ui <- function(initial_theme) {
tagList(
navbarPage(
title = tagList(
# Logo显示(更大尺寸)
tags$img(src="logo.png",
alt="YuanSeq",
style="height: 60px; margin-right: 15px; vertical-align: middle;",
class="img-responsive",
id="navbar-logo",
onerror = "this.style.display='none'; document.getElementById('navbar-title').style.display='inline';"
),
# 文字标题(logo加载失败时显示)
tags$span(id="navbar-title",
style="vertical-align: middle; display: none; color: #007AFF;",
"YuanSeq")
),
id = "navbar",
theme = bslib::bs_theme(version = 5),
header = tagList(
sci_fi_css,
theme_js,
useShinyjs()
),
collapsible = TRUE,
tabPanel("分析工作台",
sidebarLayout(
sidebarPanel(
width = 3,
class = "sidebar-panel",
materialSwitch(
inputId = "theme_toggle",
label = span("🌙 夜间模式"),
value = FALSE,
status = "primary"
),
tags$hr(),
h4("数据设置"),
radioButtons("data_source", "数据来源",
choices = c("原始Counts矩阵" = "counts",
"差异基因结果" = "deg",
"芯片差异结果" = "chip"),
inline = TRUE),
conditionalPanel(
condition = "input.data_source == 'counts'",
fileInput("file", "上传 Counts Matrix (CSV)", accept = c(".csv")),
selectInput("species_select", "物种", choices = c("Mouse (org.Mm.eg.db)" = "Mm", "Human (org.Hs.eg.db)" = "Hs")),
uiOutput("group_selector"),
# 样本数量显示
uiOutput("sample_count_display"),
tags$hr(),
h4("差异参数"),
selectInput("pval_type", "显著性指标", choices = c("padj", "pvalue")),
splitLayout(
numericInput("pval_cutoff", "P阈值", 0.05, step = 0.01),
numericInput("log2fc_cutoff", "FC阈值", 1, step = 0.5)
),
numericInput("edgeR_dispersion", "edgeR 离散度 (Dispersion^0.5)",
value = 0.1, min = 0, max = 1, step = 0.01),
helpText(class="text-muted", "注:分析方法将根据样本数量自动选择。每组样本数≥3时使用limma-voom,<3时使用edgeR。"),
# 方法选择已改为自动,隐藏原选择器
hidden(selectInput("method", "方法", choices = c("limma-voom", "edgeR"), selected = "limma-voom"))
),
conditionalPanel(
condition = "input.data_source == 'deg'",
fileInput("deg_file", "上传差异基因 (CSV)", accept = c(".csv")),
selectInput("deg_species", "物种", choices = c("Mouse (org.Mm.eg.db)" = "Mm", "Human (org.Hs.eg.db)" = "Hs")),
helpText(class="text-muted",
"CSV文件应包含以下列:p_val, avg_log2FC, pct.1, pct.2, p_val_adj, gene"),
tags$hr(),
h4("差异参数"),
selectInput("deg_pval_type", "显著性指标",
choices = c("调整P值" = "p_val_adj", "原始P值" = "p_val")),
splitLayout(
numericInput("deg_pval_cutoff", "P阈值", 0.05, step = 0.01),
numericInput("deg_log2fc_cutoff", "FC阈值", 0.25, step = 0.1)
),
tags$hr(),
actionButton("load_deg", "加载差异基因", class = "btn-info", width = "100%")
),
# 🆕 芯片差异结果导入
conditionalPanel(
condition = "input.data_source == 'chip'",
fileInput("chip_file", "上传芯片差异结果 (CSV)", accept = c(".csv")),
selectInput("chip_species", "物种", choices = c("Mouse (org.Mm.eg.db)" = "mmu", "Human (org.Hs.eg.db)" = "hsa")),
helpText(class="text-info",
"CSV文件应包含以下列:logFC, AveExpr, t, P.Value, adj.P.Val, B, SYMBOL, ID",
br(),
"这是从芯片分析模块导出的limma差异分析结果。"),
tags$hr(),
h4("差异参数"),
selectInput("chip_pval_type", "显著性指标",
choices = c("调整P值" = "adj.P.Val", "原始P值" = "P.Value")),
splitLayout(
numericInput("chip_pval_cutoff", "P阈值", 0.05, step = 0.01),
numericInput("chip_log2fc_cutoff", "FC阈值", 1, step = 0.5)
),
tags$hr(),
actionButton("load_chip", "加载芯片差异结果", class = "btn-primary", width="100%")
),
tags$hr(),
actionButton("analyze", "开始分析", class="btn-success", width="100%"),
br(), br(),
downloadButton("download_results", "下载结果", class="btn-info", style="width:100%")
),
mainPanel(
class="main-panel",
tabsetPanel(
tabPanel("📊 数据概览", icon = icon("table"),
br(),
uiOutput("deg_summary"), # 🆕 差异基因统计信息
tags$hr(),
DT::dataTableOutput("deg_table")
),
tabPanel("🌋 火山图", icon = icon("chart-area"),
fluidRow(
column(9,
plotlyOutput("interactive_volcano", height = "600px"),
tags$br(),
wellPanel(
h4("📤 导出火山图"),
splitLayout(
numericInput("export_width", "宽度 (英寸)", 10, min = 5, max = 20),
numericInput("export_height", "高度 (英寸)", 8, min = 5, max = 20)
),
radioButtons("export_format", "导出格式",
choices = c("PNG" = "png", "PDF" = "pdf", "SVG" = "svg"),
selected = "png", inline = TRUE),
downloadButton("download_volcano", "📥 下载火山图",
class = "btn-success", style = "width:100%")
),
tags$br(),
),
column(3,
wellPanel(
h4("🎨 火山图设置"),
tags$hr(),
h4("🔍 基因标签设置"),
textInput("custom_genes_input", "自定义显示基因 (逗号分隔)",
placeholder = "例如: TP53, MYC, EGFR"),
actionButton("show_custom_genes", "显示自定义基因",
class = "btn-primary", style = "width:100%"),
actionButton("clear_custom_genes", "清除自定义基因",
class = "btn-warning", style = "width:100%; margin-top: 5px;"),
tags$hr(),
h4("🔧 标签样式设置"),
sliderInput("gene_label_size", "标签字体大小", 8, 16, 10),
checkboxInput("gene_label_bold", "标签加粗", value = FALSE),
colourInput("gene_label_color", "标签颜色", "#2c3e50"),
tags$hr(),
h4("🔧 点样式设置"),
sliderInput("point_size", "点大小", 5, 15, 6),
sliderInput("point_alpha", "点透明度", 0.1, 1, 0.7),
tags$hr(),
h4("📊 坐标轴设置"),
selectInput("y_axis_type", "Y轴类型",
choices = c("-log10(pvalue)" = "pvalue",
"-log10(padj)" = "padj"),
selected = "pvalue"),
splitLayout(
numericInput("x_axis_min", "X轴最小值", -10),
numericInput("x_axis_max", "X轴最大值", 15)
),
sliderInput("axis_label_size", "坐标轴标签大小", 10, 20, 14),
sliderInput("axis_title_size", "坐标轴标题大小", 12, 24, 16),
tags$hr(),
h4("🎨 颜色设置"),
colourInput("up_color", "上调颜色", "#e74c3c"),
colourInput("down_color", "下调颜色", "#3498db"),
tags$hr(),
checkboxInput("show_grid", "显示背景方格", value = TRUE)
)
)
)
),
tabPanel("🧬 KEGG富集", icon = icon("project-diagram"),
tabsetPanel(
tabPanel("基于差异基因",
sidebarLayout(
sidebarPanel(
width = 3,
class = "sidebar-panel",
h4("🔧 分析设置"),
radioButtons("kegg_direction", "方向",
choices = c("上调" = "Up", "下调" = "Down", "全部" = "All"),
selected = "Up"),
selectInput("kegg_species", "物种代码", choices = c("hsa", "mmu")),
numericInput("kegg_p", "P Cutoff", 0.05),
actionButton("run_kegg", "运行 KEGG", class = "btn-warning"),
tags$hr(),
h4("🎨 绘图样式"),
selectInput("kegg_x_axis", "X轴显示",
choices = c("基因数量" = "Count",
"富集倍数" = "FoldEnrichment"),
selected = "Count"),
sliderInput("kegg_font_size", "字体大小", 10, 20, 12),
checkboxInput("kegg_bold", "字体加粗", value = FALSE),
colourInput("kegg_low_col", "低显著颜色 (Low)", "#3498db"),
colourInput("kegg_high_col", "高显著颜色 (High)", "#e74c3c"),
tags$hr(),
h5("📤 导出图表"),
radioButtons("kegg_export_format", "导出格式",
choices = c("PNG" = "png", "PDF" = "pdf", "SVG" = "svg"),
selected = "png", inline = TRUE),
downloadButton("download_kegg_plot", "下载KEGG图表", class="btn-sm btn-success"),
tags$hr(),
downloadButton("download_kegg", "下载结果表", class="btn-sm btn-light"),
),
mainPanel(
class = "main-panel",
plotOutput("kegg_dotplot", height = "600px"),
tags$br(),
h4("详细结果表 (ID已转Symbol)"),
DT::dataTableOutput("kegg_table"),
)
)
),
tabPanel("基于基因列表",
sidebarLayout(
sidebarPanel(
width = 3,
class = "sidebar-panel",
h4("🔧 单列基因 KEGG 分析"),
fileInput("single_gene_file", "上传基因列表 (CSV)",
accept = c(".csv"),
placeholder = "包含 SYMBOL 列的文件"),
helpText(class="text-muted",
"CSV 文件应包含一列名为 'SYMBOL' 的基因符号"),
selectInput("single_gene_species", "物种代码",
choices = c("Human" = "hsa", "Mouse" = "mmu")),
numericInput("single_gene_kegg_p", "P Cutoff", 0.05),
actionButton("run_single_gene_kegg", "运行 KEGG", class = "btn-primary"),
tags$hr(),
h4("🎨 绘图样式"),
selectInput("single_gene_kegg_x_axis", "X轴显示",
choices = c("基因数量" = "Count",
"富集倍数" = "FoldEnrichment"),
selected = "Count"),
sliderInput("single_gene_kegg_font_size", "字体大小", 10, 20, 12),
checkboxInput("single_gene_kegg_bold", "字体加粗", value = FALSE),
colourInput("single_gene_kegg_low_col", "低显著颜色 (Low)", "#3498db"),
colourInput("single_gene_kegg_high_col", "高显著颜色 (High)", "#e74c3c"),
tags$hr(),
h5("📤 导出图表"),
radioButtons("single_gene_kegg_export_format", "导出格式",
choices = c("PNG" = "png", "PDF" = "pdf", "SVG" = "svg"),
selected = "png", inline = TRUE),
downloadButton("download_single_gene_kegg_plot", "下载KEGG图表", class="btn-sm btn-success"),
tags$hr(),
downloadButton("download_single_gene_kegg", "下载结果表", class="btn-sm btn-light")
),
mainPanel(
class = "main-panel",
plotOutput("single_gene_kegg_dotplot", height = "600px"),
tags$br(),
h4("详细结果表 (ID已转Symbol)"),
DT::dataTableOutput("single_gene_kegg_table")
)
)
)
)
),
tabPanel("🧬 GO分析", icon = icon("dna"),
tabsetPanel(
tabPanel("基于差异基因",
sidebarLayout(
sidebarPanel(
width = 3,
class = "sidebar-panel",
h4("🔧 分析设置"),
radioButtons("go_direction", "方向",
choices = c("上调" = "Up", "下调" = "Down", "全部" = "All"),
selected = "Up"),
selectInput("go_species", "物种代码", choices = c("hsa", "mmu")),
selectInput("go_ontology", "GO类别",
choices = c("生物过程" = "BP",
"分子功能" = "MF",
"细胞组分" = "CC"),
selected = "BP"),
numericInput("go_p", "P Cutoff", 0.05),
numericInput("go_top_n", "显示Top N", 20, min = 5, max = 50),
actionButton("run_go", "运行 GO分析", class = "btn-warning"),
tags$hr(),
h4("🎨 绘图样式"),
selectInput("go_x_axis", "X轴属性",
choices = c("Gene Ratio" = "GeneRatio",
"Background Ratio" = "BgRatio",
"Gene Count" = "Count",
"Fold Enrichment" = "FoldEnrichment"),
selected = "GeneRatio"),
sliderInput("go_font_size", "字体大小", 10, 20, 12),
checkboxInput("go_bold", "字体加粗", value = FALSE),
colourInput("go_low_col", "低显著颜色 (Low)", "#3498db"),
colourInput("go_high_col", "高显著颜色 (High)", "#e74c3c"),
tags$hr(),
h5("📤 导出图表"),
radioButtons("go_export_format", "导出格式",
choices = c("PNG" = "png", "PDF" = "pdf", "SVG" = "svg"),
selected = "png", inline = TRUE),
downloadButton("download_go_plot", "下载GO图表", class="btn-sm btn-success"),
tags$hr(),
downloadButton("download_go", "下载结果表", class="btn-sm btn-light"),
),
mainPanel(
class = "main-panel",
plotOutput("go_dotplot", height = "600px"),
tags$br(),
h4("详细结果表"),
DT::dataTableOutput("go_table"),
)
)
),
tabPanel("基于基因列表",
sidebarLayout(
sidebarPanel(
width = 3,
class = "sidebar-panel",
h4("🔧 单列基因 GO 分析"),
fileInput("single_gene_go_file", "上传基因列表 (CSV)",
accept = c(".csv"),
placeholder = "包含 SYMBOL 列的文件"),
helpText(class="text-muted",
"CSV 文件应包含一列名为 'SYMBOL' 的基因符号"),
selectInput("single_gene_species_go", "物种代码",
choices = c("Human" = "hsa", "Mouse" = "mmu")),
selectInput("single_gene_go_ontology", "GO类别",
choices = c("生物过程" = "BP",
"分子功能" = "MF",
"细胞组分" = "CC"),
selected = "BP"),
numericInput("single_gene_go_p", "P Cutoff", 0.05),
actionButton("run_single_gene_go", "运行 GO分析", class = "btn-primary"),
tags$hr(),
h4("🎨 绘图样式"),
selectInput("single_gene_go_x_axis", "X轴属性",
choices = c("Gene Ratio" = "GeneRatio",
"Background Ratio" = "BgRatio",
"Gene Count" = "Count",
"Fold Enrichment" = "FoldEnrichment"),
selected = "GeneRatio"),
sliderInput("single_gene_go_font_size", "字体大小", 10, 20, 12),
checkboxInput("single_gene_go_bold", "字体加粗", value = FALSE),
colourInput("single_gene_go_low_col", "低显著颜色 (Low)", "#3498db"),
colourInput("single_gene_go_high_col", "高显著颜色 (High)", "#e74c3c"),
tags$hr(),
h5("📤 导出图表"),
radioButtons("single_gene_go_export_format", "导出格式",
choices = c("PNG" = "png", "PDF" = "pdf", "SVG" = "svg"),
selected = "png", inline = TRUE),
downloadButton("download_single_gene_go_plot", "下载GO图表", class="btn-sm btn-success"),
tags$hr(),
downloadButton("download_single_gene_go", "下载结果表", class="btn-sm btn-light")
),
mainPanel(
class = "main-panel",
plotOutput("single_gene_go_dotplot", height = "600px"),
tags$br(),
h4("详细结果表"),
DT::dataTableOutput("single_gene_go_table")
)
)
)
)
),
tabPanel("🌊 GSEA分析", icon = icon("water"),
sidebarLayout(
sidebarPanel(
width = 3,
class = "sidebar-panel",
h4("🔧 GSEA 设置"),
fileInput("gmt_file", "上传 GMT 文件", accept = c(".gmt")),
helpText(class="text-muted", "支持大文件上传 (Max 100MB)"),
selectInput("gsea_id_type", "GMT中的ID类型",
choices = c("Gene Symbol (推荐)" = "SYMBOL",
"Entrez ID" = "ENTREZID"),
selected = "SYMBOL"),
helpText(class="text-info small",
"💡 推荐使用Symbol以在图上显示基因名称(如Csf3)"),
numericInput("gsea_pvalue", "Pvalue Cutoff", 0.05),
actionButton("run_gsea", "运行 GSEA", class = "btn-primary"),
tags$div(
class = "alert alert-info",
style = "margin-top: 12px; margin-bottom: 0; padding: 10px; font-size: 0.9em;",
tags$strong("💡 延伸分析:GPSA"),
tags$p(
style = "margin: 6px 0 8px 0;",
"能做 GSEA 时,推荐在 ",
tags$a(href = "https://www.gpsadb.com/", target = "_blank", rel = "noopener noreferrer", "GPSAdb"),
" 上再做一次 ",
tags$a(href = "https://www.gpsadb.com/", target = "_blank", rel = "noopener noreferrer", "fastGPSA"),
" 分析:可发现「哪个基因的敲减/扰动会产生类似的下游转录组效应」,有助于发现分子伴侣与上下游调控分子。"
),
tags$a(
href = "https://www.gpsadb.com/",
target = "_blank",
rel = "noopener noreferrer",
class = "btn btn-sm btn-outline-primary",
style = "margin-top: 4px;",
"打开 GPSAdb →"
)
),
tags$hr(),
h4("🎨 绘图调整"),
p(class="text-muted", "拖动滑块调整统计值位置"),
sliderInput("gsea_stats_x", "统计值水平位置 (X)", min = 0, max = 1, value = 0.6, step = 0.05),
sliderInput("gsea_stats_y", "统计值垂直位置 (Y)", min = 0, max = 1, value = 0.8, step = 0.05),
tags$hr(),
h4("🧬 基因展示设置"),
p(class="text-muted", "在GSEA图上标记Leading Edge基因"),
numericInput("gsea_top_genes", "自动标记Top N基因", value = 20, min = 5, max = 100, step = 5),
selectInput("gsea_gene_order", "基因排序方式",
choices = c("🔥 GSEA Leading Edge基因(顶峰基因)" = "leading_edge",
"按log2FoldChange绝对值" = "abs_logFC",
"按log2FoldChange值" = "logFC",
"按基因在ranked list中的位置" = "rank"),
selected = "leading_edge"),
textInput("custom_gene_list", "自定义基因列表 (可选)",
placeholder = "例如: Entpd8, Htr2a, Nt5e, Actn3"),
helpText(class="text-muted small",
"留空则自动显示Top N基因。支持逗号、分号或空格分隔。"),
tags$hr(),
h4("📊 多通路可视化"),
p(class="text-muted", "使用gseaRidge展示多个通路的基因分布"),
numericInput("gsea_ridge_pathways", "选择Top N通路", value = 10, min = 3, max = 20, step = 1),
checkboxInput("show_gsea_ridge", "显示山脊图", value = FALSE),
tags$hr(),
h4("📤 GSEA结果导出"),
div(class = "gsea-export-section",
p(strong("导出选项:")),
radioButtons("gsea_export_type", "导出内容",
choices = c("完整结果表" = "full",
"显著结果表 (P < 0.05)" = "significant",
"Top 50 通路" = "top50"),
selected = "full"),
numericInput("gsea_export_topn", "Top N 通路 (仅Top N模式)",
value = 50, min = 10, max = 200, step = 10),
downloadButton("download_gsea_full", "📥 下载完整结果",
class = "btn-success btn-sm",
style = "width:100%; margin-bottom:5px;"),
downloadButton("download_gsea_sig", "📥 下载显著结果",
class = "btn-warning btn-sm",
style = "width:100%; margin-bottom:5px;"),
downloadButton("download_gsea_top", "📥 下载Top N结果",
class = "btn-info btn-sm",
style = "width:100%; margin-bottom:5px;"),
tags$hr(style = "margin: 10px 0;"),
# SVG下载按钮
p(class="text-primary", style="font-weight:bold", "📊 导出图表 (SVG格式)"),
downloadButton("download_gsea_plot_svg", "📥 下载GSEA图 (SVG)",
class = "btn-primary btn-sm",
style = "width:100%; margin-bottom:5px;"),
downloadButton("download_gsea_ridge_svg", "📥 下载山脊图 (SVG)",
class = "btn-primary btn-sm",
style = "width:100%;")
),
tags$hr(),
p(class="text-muted", "注:GSEA 基于 log2FoldChange 排序。SVG格式可无损缩放,适合出版。")
),
mainPanel(
class = "main-panel",
fluidRow(
column(12,
h4("GseaVis 可视化"),
p(class="text-muted", "点击下方表格中的某一行,即可显示对应的 GSEA 图,图中会自动标记Leading Edge基因(红色斜体标注)。"),
p(class="text-info small", "💡 做完 GSEA 后,可到 ",
tags$a(href = "https://www.gpsadb.com/", target = "_blank", rel = "noopener noreferrer", "GPSAdb"), " 使用 fastGPSA 上传同一排序基因列表,查找产生类似下游效应的敲减/扰动基因。"),
plotOutput("gsea_plot", height = "600px")
)
),
tags$hr(),
# 山脊图输出
conditionalPanel(
condition = "input.show_gsea_ridge",
fluidRow(
column(12,
h4("GseaRidge 多通路山脊图"),
p(class="text-muted", "展示Top N个GSEA通路的基因分布山脊图"),
plotOutput("gsea_ridge_plot", height = "800px")
)
),
tags$hr()
),
h4("GSEA 结果表"),
DT::dataTableOutput("gsea_table")
)
)
),
tabPanel("🔬 转录因子活性", icon = icon("dna"),
sidebarLayout(
sidebarPanel(
width = 3,
class = "sidebar-panel",
h4("🧬 TF 活性推断"),
helpText(class="text-muted", "使用 decoupleR 推断转录因子活性。"),
# 🆕 算法选择
selectInput("tf_method", "推断算法",
choices = c(
"ULM (单变量线性模型)" = "ulm",
"MLM (多变量线性模型)" = "mlm",
"WMEAN (加权平均)" = "wmean",
"WSUM (加权求和)" = "wsum"
),
selected = "ulm"),
helpText(class="text-muted small", "ULM: 快速准确 | MLM: 考虑共调控 | WMEAN/WSUM: 简单加权"),
numericInput("tf_min_size", "TF靶基因最小数量", 5, min = 1, step = 1),
tags$hr(),
actionButton("run_tf_activity", "运行 TF 活性分析", class = "btn-info"),
tags$hr(),
h4("🎨 绘图样式"),
sliderInput("tf_top_n", "显示 Top N 个TF", 10, 50, 20),
colourInput("tf_active_col", "激活颜色", "#e74c3c"),
colourInput("tf_inactive_col", "失活颜色", "#3498db"),
tags$hr(),
h5("🎯 网络图自定义"),
sliderInput("tf_network_node_size", "节点大小倍数", 0.5, 3, 1, step = 0.1),
sliderInput("tf_network_label_size", "标签大小", 2, 6, 3.5, step = 0.5),
colourInput("tf_tf_node_col", "TF节点颜色", "#2ecc71"),
colourInput("tf_consistent_act_col", "一致-激活节点", "#27ae60"),
colourInput("tf_consistent_rep_col", "一致-抑制节点", "#2980b9"),
colourInput("tf_inconsistent_act_col", "不一致-激活节点", "#c0392b"),
colourInput("tf_inconsistent_rep_col", "不一致-抑制节点", "#2c3e50"),
colourInput("tf_neutral_col", "未知节点", "#95a5a6"),
tags$hr(),
h5("🎯 散点图自定义"),
sliderInput("tf_scatter_point_size", "散点大小", 1, 6, 3, step = 0.5),
sliderInput("tf_scatter_alpha", "散点透明度", 0.1, 1, 0.7, step = 0.1),
sliderInput("tf_scatter_label_size", "标签大小", 2, 6, 3, step = 0.5),
sliderInput("tf_scatter_n_labels", "显示标签数量", 0, 30, 15, step = 1),
tags$hr(),
downloadButton("download_tf_results", "下载 TF 结果表", class="btn-sm btn-light")
),
mainPanel(
class = "main-panel",
fluidRow(
column(12,
h4("TF 活性变化柱状图 (Treatment vs Control)"),
plotOutput("tf_activity_bar_plot", height = "600px")
)
),
tags$hr(),
h4("转录因子活性结果表"),
DT::dataTableOutput("tf_activity_table"),
tags$hr(),
h4("所选 TF 靶基因调控一致性散点图"),
p(class="text-muted", "图中的颜色显示了靶基因的实际差异表达方向与 TF 调控网络预设方向的匹配程度。"),
# 🆕 一致性统计
uiOutput("tf_consistency_summary"),
tags$br(),
plotOutput("tf_target_plot", height = "600px"),
tags$hr(),
h4("所选 TF 靶基因调控网络"),
p(class="text-muted", "显示TF与其靶基因的调控关系。红色=激活,蓝色=抑制,实线=一致,虚线=不一致。"),
# 🆕 交互式网络图
plotlyOutput("tf_network_plot_interactive", height = "700px"),
# 静态图用于导出
hidden(plotOutput("tf_network_plot_static", height = "700px")),
tags$br(),
checkboxInput("use_static_network", "使用静态图(用于SVG导出)", FALSE),
# 🆕 SVG下载按钮
downloadButton("download_tf_network_svg", "📥 下载网络图 (SVG)", class = "btn-sm btn-primary"),
downloadButton("download_tf_scatter_svg", "📥 下载散点图 (SVG)", class = "btn-sm btn-primary"),
# 🆕 数据导出按钮
downloadButton("download_tf_scatter_data", "💾 导出靶基因数据 (CSV)", class = "btn-sm btn-success"),
tags$hr(),
h4("所选 TF 的靶基因详细信息"),
p(class="text-muted", "请在上方 '转录因子活性结果表' 中点击一行,查看其靶基因。"),
DT::dataTableOutput("tf_target_table")
)
)
),
tabPanel("🔷 韦恩图", icon = icon("object-group"),
sidebarLayout(
sidebarPanel(
width = 3,
class = "sidebar-panel",
h4("🔷 韦恩图设置"),
numericInput("venn_sets", "集合数量",
value = 2, min = 2, max = 5, step = 1),
uiOutput("venn_inputs"),
tags$hr(),
h4("🎨 绘图样式"),
colourInput("venn_color1", "集合1颜色", "#1f77b4"),
colourInput("venn_color2", "集合2颜色", "#ff7f0e"),
colourInput("venn_color3", "集合3颜色", "#2ca02c"),
colourInput("venn_color4", "集合4颜色", "#d62728"),
colourInput("venn_color5", "集合5颜色", "#9467bd"),
sliderInput("venn_alpha", "透明度", 0.3, 0.8, 0.5),
tags$hr(),
actionButton("generate_venn", "生成韦恩图",
class = "btn-primary btn-lg",
width = "100%")
),
mainPanel(
class = "main-panel",
fluidRow(
column(12,
h4("交互式韦恩图"),
p(class="text-muted",
"点击图中的交集区域查看详细信息,交集内容会自动复制到剪贴板。"),
plotOutput("venn_plot",
click = "venn_click",
height = "500px"),
tags$br(),
uiOutput("venn_result_ui"),
tags$hr(),
h4("详细交集数据"),
DT::dataTableOutput("venn_table")
)
)
)
)
),
# 🆕 通路活性分析
tabPanel("🛤️ 通路活性", icon = icon("project-diagram"),
sidebarLayout(
sidebarPanel(
width = 3,
class = "sidebar-panel",
h4("🧬 KEGG 通路活性推断"),
helpText(class="text-muted", "基于 KEGG 富集结果推断通路活性。"),
tags$hr(),
# 方法选择
selectInput("pathway_method", "推断方法",
choices = c(
"ULM (推荐)" = "ulm",
"WMEAN (加权平均)" = "wmean"
# "AUCell" = "aucell", # ❌ 需要多样本表达矩阵
# "GSVA" = "gsva" # ❌ 需要多样本表达矩阵
),
selected = "ulm"),
# 方法说明
uiOutput("pathway_method_info"),
# 警告信息
helpText(class="text-muted",
style = "color: #f39c12; font-size: 11px;",
"⚠️ 注意: AUCell和GSVA方法需要原始表达矩阵,暂不可用。"),
tags$hr(),
# 最小基因集大小
numericInput("pathway_minsize", "最小基因集大小",
value = 5, min = 3, max = 50),
helpText(class="text-muted",
"通路至少包含的基因数量"),
tags$hr(),
# 显示数量
sliderInput("pathway_top_n", "显示 Top N 通路",
min = 5, max = 50, value = 20, step = 5),
tags$hr(),
# 颜色设置
colourInput("pathway_active_col", "激活通路颜色",
value = "#e74c3c"),
colourInput("pathway_inactive_col", "抑制通路颜色",
value = "#3498db"),
tags$hr(),
# 热图字体大小设置
numericInput("pathway_heatmap_fontsize", "热图字体大小",
value = 10, min = 6, max = 20, step = 1),
helpText(class="text-muted",
"调整热图中基因名和分数的字体大小"),
# 柱状图字体大小设置
numericInput("pathway_bar_fontsize", "柱状图字体大小",
value = 10, min = 6, max = 20, step = 1),
helpText(class="text-muted",
"调整柱状图中的标题、轴标签和图例字体大小"),
tags$hr(),
# 运行按钮
actionButton("run_pathway_activity", "🚀 运行通路活性分析",
class = "btn-primary btn-block",
icon = icon("play"),
style = "margin-top: 10px;"),
# 统计摘要
uiOutput("pathway_summary"),
tags$hr(),
# 下载按钮
downloadButton("download_pathway_results", "📥 下载结果",
class = "btn-success btn-block"),
helpText(class="text-muted",
"💡 提示:请先运行 KEGG 富集分析",
style = "color: #f39c12; font-size: 11px;")
),
mainPanel(
class = "main-panel",
# 结果展示
tabsetPanel(
id = "pathway_tabs",
type = "tabs",
# 算法说明
tabPanel("📖 算法说明",
h4("🧠 什么是通路活性分析?"),
uiOutput("pathway_algorithm_intro"),
tags$hr(),
h4("🔬 ULM 方法原理"),
uiOutput("pathway_ulm_explanation"),
tags$hr(),
h4("📊 结果解读指南"),
uiOutput("pathway_result_guide"),
tags$hr(),
h4("💡 实际应用案例"),
uiOutput("pathway_example_use_case"),
tags$hr(),
h4("❓ 常见问题"),
uiOutput("pathway_faq")
),
# 柱状图
tabPanel("柱状图",
fluidRow(
column(10,
h4("通路活性排名")
),
column(2,
align = "right",
downloadButton("download_pathway_bar_png",
"📥 PNG",
class = "btn-sm btn-primary"),
downloadButton("download_pathway_bar_svg",
"📥 SVG",
class = "btn-sm btn-info")
)
),
plotOutput("pathway_activity_bar_plot",
height = "600px"),
tags$br(),
h4("详细结果表"),
DT::dataTableOutput("pathway_activity_table")
),
# 热图
tabPanel("热图",
fluidRow(
column(10,
h4("通路活性热图")
),
column(2,
align = "right",
downloadButton("download_pathway_heatmap_png",
"📥 PNG",
class = "btn-sm btn-primary"),
downloadButton("download_pathway_heatmap_svg",
"📥 SVG",
class = "btn-sm btn-info")
)
),
plotOutput("pathway_activity_heatmap",
height = "600px")
)
)
)
)
),
# 🆕 芯片数据分析
tabPanel("🧬 芯片分析", icon = icon("microchip"),
uiOutput("chip_analysis_ui_output")
)
)
)
)
),
# 开发者信息 Developer credits(双语,导师链接至学院主页)
tags$div(
class = "xseq-developer-footer",
style = "padding: 12px 15px; text-align: center; font-size: 12px; color: #6c757d; border-top: 1px solid #dee2e6; margin-top: 8px; background: rgba(0,0,0,0.02);",
tags$p(style = "margin: 0; font-weight: 600;", "开发者 Developer: 乔宇 Yu Qiao"),
tags$p(style = "margin: 2px 0 0 0;", "上海交通大学药学院 · 药理学博士 | School of Pharmacy, Shanghai Jiao Tong University · PhD in Pharmacology"),
tags$p(style = "margin: 4px 0 0 0;",
"导师 Supervisors: ",
tags$a(href = "https://pharm.sjtu.edu.cn/szdy/2862.html", target = "_blank", rel = "noopener noreferrer", "钱峰教授 Prof. Feng Qian"),
"、",
tags$a(href = "https://pharm.sjtu.edu.cn/szdy/2870.html", target = "_blank", rel = "noopener noreferrer", "孙磊教授 Prof. Lei Sun")
),
tags$p(style = "margin: 6px 0 0 0; font-size: 11px; opacity: 0.9;",
"饮水思源 · 致谢 R/Bioconductor 及本平台所集成的开源包 | Thanks to R, Bioconductor & all integrated packages")
)
)
}