- 專案整體架構
- 技術棧與依賴
- 檔案結構與組織
- Vue 應用初始化流程
- 元件設計架構
- Mixin 模組化設計
- 表單欄位配置系統
- 資料流與狀態管理
- 批次處理與串流機制
- UI 互動與主題系統
- 語法高亮與日誌系統
- 資料持久化機制
- 進階功能與輔助機制
- API 介接設計
- 開發與維護指南
PowerBarcoder 前端採用模組化架構設計,以 Vue.js 2.x 為核心框架,結合 Mixin 模式實現功能模組化。整體架構遵循以下設計原則:
- 模組化分離:功能按職責分離到不同 Mixin 中
- 元件化開發:UI 元件獨立設計,支援重複使用
- 配置驅動:表單欄位透過配置檔案動態生成
- 響應式設計:支援深色/淺色主題切換
- 狀態持久化:關鍵狀態與日誌資料本機儲存
- 可恢復性:頁面 reload 可在不丟失日誌的情況下恢復 batch 觀察
┌─────────────────────────────────────────────────────────────┐
│ Vue App (App.js) │
├─────────────────────────────────────────────────────────────┤
│ FormHandler │ StreamHandler │ UIInteractions │ Storage │
│ Mixin │ Mixin │ Mixin │ Mixin │
├─────────────────────────────────────────────────────────────┤
│ form-group │ modal │ path-form │ locus-form │ llm-form │
│ Component │ Component | Component │ Component │ Component │
├─────────────────────────────────────────────────────────────┤
│ Templates (form-group.js, modal.js, etc.) │
├─────────────────────────────────────────────────────────────┤
│ Utils (SyntaxHighlighter) │ Config (Fields) │
└─────────────────────────────────────────────────────────────┘
- Vue.js 2.6.14:前端 MVVM 框架,負責資料綁定與元件化
- Bootstrap 5.1.3:UI 框架,提供響應式設計與元件樣式
- jQuery 3.6.0:輔助 DOM 操作與事件處理
- highlight.js 11.3.1:程式碼與日誌語法高亮
- Font Awesome 4.7.0:圖示庫
- IndexedDB:客戶端資料庫,用於日誌持久化儲存
- localStorage:本機儲存,保存用戶偏好與狀態
- EventSource (SSE):伺服器推送事件,即時接收處理日誌
- Fetch API:HTTP 請求,與後端 API 通訊
- ES6 Modules:現代 JavaScript 模組系統
- Template Literals:HTML 模板字串
- Async/Await:異步操作處理
static/
├── css/
│ └── styles.css # 自訂樣式表
├── js/
│ ├── components/ # Vue 元件與 Mixin
│ │ ├── App.js # 主應用程式
│ │ ├── BaseComponent.js # 基礎元件定義
│ │ ├── FormHandler.js # 表單處理 Mixin
│ │ ├── LLMSequenceAnalysisModal.js # LLM 序列分析模態視窗
│ │ ├── PersistentStorage.js # 資料持久化 Mixin
│ │ ├── StreamHandler.js # 串流處理 Mixin
│ │ └── UIInteractions.js # UI 互動 Mixin
│ ├── templates/ # 元件 HTML 模板
│ │ ├── form-group.js # 表單群組模板
│ │ ├── llm-form.js # LLM 分析表單模板
│ │ ├── locus-form.js # 座標表單模板
│ │ ├── modal.js # 模態視窗模板
│ │ ├── path-form.js # 路徑表單模板
│ │ └── sequence-extraction-form.js # 序列萃取表單模板
│ ├── utils/ # 工具模組
│ │ └── SyntaxHighlighter.js # 語法高亮工具
│ ├── llm-fields.js # LLM 欄位配置
│ ├── locus-fields.js # 座標欄位配置
│ ├── path-fields.js # 路徑欄位配置
│ ├── sequence-extraction-fields.js # 序列萃取欄位配置
│ └── vue-components.js # 應用程式入口
└── templates/
└── index.html # 主頁面 HTML
- App.js:整合所有功能,作為 Vue 應用程式主入口
- BaseComponent.js:定義可重複使用的基礎元件
- Mixin 檔案:按功能職責分離的邏輯模組
- Templates:元件的 HTML 模板,使用 ES6 Template String
- Config 檔案:表單欄位結構定義,支援動態表單生成
stateDiagram-v2
[*] --> idle
idle --> validating : submit click
validating --> submitting : ok
validating --> idle : invalid
submitting --> running : got batch_id + SSE open
submitting --> error : HTTP fail
running --> reconnecting : SSE error
reconnecting --> running : SSE re-open
running --> finished : batch-status=completed
running --> failed : batch-status=failed
error --> idle : user fixes
failed --> idle : new submit
finished --> idle : new submit
- HTML 載入:
index.html載入所有依賴資源 - 配置初始化:載入
CONFIG全域配置物件 - 模組載入:
vue-components.js匯入所有模組 - 語法高亮初始化:設定 highlight.js 主題
- Vue 實例建立:
createApp()建立並掛載 Vue 應用
// 1. 載入模組
import { createApp } from './components/App.js';
import { SyntaxHighlighter } from './utils/SyntaxHighlighter.js';
// 2. 註冊全域工具
window.SyntaxHighlighter = SyntaxHighlighter;
// 3. 初始化主題
const savedDarkMode = localStorage.getItem('darkMode') === 'true';
SyntaxHighlighter.init(savedDarkMode);
// 4. 建立 Vue 應用
window.app = createApp();export function createApp() {
return new Vue({
el: '#app',
mixins: [FormHandlerMixin, StreamHandlerMixin, UIInteractionsMixin, PersistentStorageMixin],
data: {
// 表單資料
formData: { ... },
// 狀態管理
isSubmitting: false,
darkMode: false,
// 批次處理
currentBatchId: null,
isProcessRunning: false
},
// 生命週期與監聽器
watch: { ... },
mounted() { ... }
});
}所有元件透過 registerBaseComponent 函數統一註冊:
export function registerBaseComponent(Vue) {
Vue.component('form-group', FormGroupComponent);
Vue.component('modal', ModalComponent);
Vue.component('path-form', PathFormComponent);
Vue.component('locus-form', LocusFormComponent);
}FormGroupComponent 為基礎表單欄位元件,支援:
- 單/雙欄位模式:透過
dual屬性控制 - 輔助欄位:
secondary配置支援輔助輸入 - 自動同步:
secondaryDisabled時自動同步主欄位值 - 動態屬性:ID、Class、Name 自動生成或可自訂
- 驗證支援:必填欄位驗證(batch_id 與 dada2LearnErrorFile 除外)
ModalComponent 負責日誌顯示與狀態管理:
- 全螢幕模態視窗:支援大量日誌內容顯示
- 即時日誌更新:透過 SSE 接收並顯示處理進度
- 主題切換:深色/淺色模式切換
- 操作按鈕:下載結果、清除日誌、保存日誌等功能
LLMSequenceAnalysisModal 提供 LLM 序列分析功能:
- 參數設定介面:支援 Batch ID、Locus、識別符類型等參數配置
- 樣本自動完成:根據 Batch ID 和 Locus 自動載入可用樣本清單
- 分析結果展示:提供結構化的分析結果顯示
- 主題適配:支援深色/淺色模式主題切換
- 即時分析:與後端 LLM 分析 API 整合,提供即時序列分析功能
主要功能:
- 根據 Sample ID 或 Barcode ID 選擇特定序列進行分析
- 自動從已完成的 Pipeline 結果中載入序列資料
- 提供 LLM 輔助的序列品質評估和建議
- 支援批次分析和結果匯出
- PathForm:處理路徑相關設定,包含資料夾路徑、檔案路徑等
- LocusForm:處理座標相關參數,支援多座標動態新增
- Demo 功能:提供 rbcL 和 trnLF 預設值快速填入
負責表單處理邏輯:
- 表單驗證:
validateAllInputs()檢查必填欄位,包含 LLM 特殊驗證- 基本路徑驗證:檢查必要的檔案路徑是否填寫
- LLM 分析驗證:當啟用 LLM 時,檢查 API Key 和模型設定
- API Key 檢查:支援環境變數或直接輸入,提供清晰的錯誤提示
- 表單提交:
handleFormSubmission()處理提交流程 - 自動填充:
autoCompleteWithLoci()支援 Demo 資料填入- 路徑欄位自動填充:
autoCompletePaths() - LLM 欄位自動填充:
autoCompleteLLMConfig() - 序列萃取欄位自動填充:
autoCompleteSequenceExtractionConfig()
- 路徑欄位自動填充:
- 錯誤處理:
handleSubmissionError()統一錯誤處理
// LLM validation - 當啟用 LLM 分析時的驗證
if (this.formData.llm_analysis_enabled) {
// 檢查 API key 是否有效
if (!this.formData.llm_api_key || this.formData.llm_api_key.trim() === '') {
alert('LLM 分析已啟用,但未設置 API Key。\n\n' +
'請進行以下操作之一:\n' +
'1. 設置環境變數 POWERBARCODER_LLM_API_KEY\n' +
'2. 在表單中直接輸入 API Key\n' +
'3. 關閉 LLM 分析功能');
return false;
}
// 檢查模型名稱是否有效
if (!this.formData.llm_model || this.formData.llm_model.trim() === '') {
alert('LLM 分析已啟用,但未設置模型名稱。請輸入有效的 LLM 模型名稱。');
return false;
}
}處理 SSE 串流與批次狀態:
- SSE 連接管理:
startEventSource()建立與維護連接 - 批次狀態檢查:
checkBatchStatus()確認批次執行狀態 - 日誌緩衝:
logBuffer機制避免頻繁 IndexedDB 寫入 - 連接恢復:頁面重載時可恢復未完成的批次連接
處理 UI 互動功能:
- 深色模式切換:
toggleDarkMode()切換並保存主題偏好 - 表單區塊摺疊:
togglePathForm()和toggleLocusForm() - 日誌操作:
clearLog()和saveLog()日誌管理 - 進階模式:
openAdvanceMode()顯示進階設定
資料持久化管理:
- IndexedDB 初始化:
initIndexedDB()建立客戶端資料庫 - 日誌儲存:
saveLogToIndexedDB()按批次儲存日誌 - 狀態恢復:
checkAndRestoreProcessingState()恢復中斷的處理 - 清理機制:超過 24 小時的批次自動清理
路徑欄位透過 path-fields.js 定義:
const pathFields = [
{
name: 'ampliconInfo',
label: 'Raw Files Folder Path:',
placeholderKey: 'default_data_dir',
infoText: "環境變數 POWERBARCODER_DATA_DIR 路徑"
},
{
name: 'R1FastqGz',
label: 'R1 fastq.gz file path:',
placeholderKey: 'default_r1_fastq_gz',
secondary: {
name: 'R1FastqGzHelper',
disabled: true,
placeholderKey: 'default_data_dir',
},
infoText: "R1 原始資料檔案路徑"
}
// ...更多欄位
];座標欄位透過 locus-fields.js 定義:
const locusFields = [
{
name: 'nameOfLoci',
label: 'Name of Loci',
placeholderKey: 'name_of_loci',
infoText: 'Locus name for your data'
},
{
name: 'primerF',
label: 'Primer F',
placeholderKey: 'primer_f',
secondary: {
name: 'primerFName',
placeholderKey: 'rbcl_primer_f_name',
},
infoText: 'Primer F sequence for this locus'
}
// ...更多欄位
];LLM 分析欄位透過 llm-fields.js 定義:
const llmFields = [
{
name: 'llm_analysis_enabled',
label: 'Enable LLM Analysis',
type: 'checkbox',
infoText: 'Enable this option to perform LLM-assisted analysis'
},
{
name: 'llm_model',
label: 'LLM Model',
type: 'text',
required: true,
placeholder: 'e.g., gpt-4, grok-3-latest, llama-3.1-8b',
infoText: 'Enter the LLM model name you want to use'
},
{
name: 'llm_api_key',
label: 'API Key',
type: 'password',
required: true,
placeholder: 'Enter API Key or set POWERBARCODER_LLM_API_KEY',
infoText: 'API key can be set via environment variable for security'
},
{
name: 'llm_base_url',
label: 'Base URL',
type: 'text',
placeholder: 'Leave empty for default',
infoText: 'API base URL, usually can be left empty'
}
// ...更多欄位
];序列萃取控制欄位透過 sequence-extraction-fields.js 定義:
const sequenceExtractionFields = [
{
name: 'enable_debug_sequence_extraction',
label: 'Enable Debug Mode Sequence Extraction',
type: 'checkbox',
defaultValue: false,
infoText: 'Enable debug mode to extract sequences from all processing steps. This provides comprehensive sequence information for debugging and analysis.'
},
{
name: 'top_k_sequences',
label: 'TOP K Sequences',
type: 'number',
min: 1,
max: 10,
defaultValue: 3,
placeholder: '3',
infoText: 'Number of top abundance sequences to retain for each processing step. Recommended value: 3-5.'
}
];序列萃取配置說明:
- enable_debug_sequence_extraction:Debug 模式開關
- 啟用時:萃取所有處理步驟的序列用於除錯分析
- 停用時:只萃取推薦序列(預設模式)
- 提供完整的序列資訊用於除錯和分析
- top_k_sequences:TOP K 序列數量控制
- 範圍:1-10
- 預設值:3
- 決定每個處理步驟保留的最高豐度序列數量
- 建議值:3-5
- name:資料模型中的欄位名稱
- label:顯示標籤
- type:欄位類型(text, checkbox, number, password 等)
- placeholderKey:對應 CONFIG 物件中的預設值鍵
- placeholder:直接指定的佔位符文字
- defaultValue:欄位預設值
- secondary:輔助欄位配置
- infoText:欄位說明文字
- disabled:是否禁用(通常用於輔助欄位)
- min/max:數值欄位的範圍限制
- required:是否為必填欄位
formData: {
// 路徑設定
ampliconInfo: '',
R1FastqGz: '',
R1FastqGzHelper: '',
// 批次設定
batch_id: '',
override_config: false,
// LLM 分析設定
llm_analysis_enabled: false,
llm_model: '',
llm_api_key: '',
llm_base_url: '',
llm_batch_size: 1,
// 序列萃取設定
enable_debug_sequence_extraction: false,
top_k_sequences: 3,
// 座標設定(陣列支援多座標)
nameOfLoci: [''],
primerF: [''],
primerR: [''],
// ...更多欄位
}PowerBarcoder 前端透過以下機制處理 LLM API Key 的安全傳遞:
# app.py 中的配置傳遞
path_manager = PathManager() # 讀取環境變數
render_template('index.html',
# ...其他配置
llm_api_key=path_manager.llm_api_key or '', # 環境變數值或空字串
# ...
)// index.html 中的配置接收
const CONFIG = {
// ...其他配置
llm_api_key: "{{ llm_api_key }}", // 從後端傳遞的環境變數值
// ...
};
// 自動填充邏輯
autoCompleteLLMConfig() {
// 如果環境變數已設定,自動填入表單
this.formData.llm_api_key = CONFIG.llm_api_key || '';
}- Vue 響應式:利用 Vue 的響應式系統自動更新 UI
- Watch 監聽:
ampliconInfo變更時自動更新輔助欄位 - 批次狀態:
currentBatchId和isProcessRunning管理處理狀態 - UI 狀態:
darkMode、pathFormVisible等控制介面顯示
用戶輸入 → FormData → 驗證 → 提交 → 批次處理 → SSE 接收 → 日誌顯示 → IndexedDB
↓ ↓
UI 更新 狀態更新
- 表單提交:驗證通過後提交至
/run-procedureAPI - 批次建立:後端回傳
batch_id - SSE 連接:建立
/stream/{batch_id}連接 - 即時更新:接收處理進度並更新 UI
- 完成處理:更新狀態並啟用下載功能
startEventSource() {
this.connectionState = 'connecting';
this.currentEventSource = new EventSource(`${HOST_URL}stream/${this.currentBatchId}`);
this.currentEventSource.onmessage = (event) => {
const logLine = event.data;
this.addLogToConsole(logLine);
this.saveLogToBuffer(logLine);
};
this.currentEventSource.onerror = () => {
this.connectionState = 'disconnected';
};
}支援檢查批次執行狀態:
- running:正在執行
- completed:執行完成
- failed:執行失敗
- not_found:批次不存在
- 主題切換:
toggleDarkMode()切換並保存偏好 - 語法高亮同步:主題變更時同步更新 highlight.js
- 控制台樣式:深色模式下調整控制台背景色
- 狀態持久化:偏好儲存至 localStorage
- PathForm 摺疊:隱藏/顯示路徑設定區塊
- LocusForm 摺疊:隱藏/顯示座標設定區塊
- Bootstrap Collapse:使用 Bootstrap 的 collapse 功能
- 進階欄位:預設隱藏,需手動啟用
- CSS 類別控制:
.advanceMode和.hideAdvanceMode - 例如:
override_config選項屬於進階功能
統一管理 highlight.js 相關操作:
export const SyntaxHighlighter = {
THEMES: {
DARK: 'obsidian.min.css',
LIGHT: 'github.min.css'
},
init(isDarkMode) {
this.setTheme(isDarkMode);
},
createHighlightedElement(text, className, isDarkMode) {
const element = $("<code>");
element.text(text);
element.addClass(className);
this.highlight(element);
return element;
}
};- 即時顯示:SSE 接收的日誌即時顯示在控制台
- 語法高亮:使用
shellsession類別高亮日誌 - 自動滾動:新日誌出現時自動滾動至底部
- 無上限顯示:移除顯示行數限制,支援大量日誌
- Buffer 設計:避免頻繁寫入 IndexedDB
- 定時保存:每 5 秒或達到 10 筆時自動保存
- 手動清理:提供清除顯示但保留歷史記錄的功能
- 資料庫名稱:
PowerBarcoderDB - 版本控制:支援結構變更升級
- 儲存表格:
logs表格儲存日誌資料 - 索引設計:按
batchId和timestamp建立索引
{
id: 'batch_id_timestamp',
batchId: 'batch_123456',
timestamp: 1641234567890,
logLines: ['log line 1', 'log line 2', ...]
}- 深色模式偏好:
darkMode - 批次狀態:
currentBatchId、isProcessRunning - 處理時間戳:
processingTimestamp - 自動清理:超過 24 小時的狀態自動清理
- Demo rbcL:快速填入 rbcL 相關預設值
- Demo trnLF:快速填入 trnLF 相關預設值
- Demo LLM:快速填入 LLM 分析相關預設值
- Demo Sequence Extraction:快速填入序列萃取推薦配置
- 路徑自動完成:基於
ampliconInfo自動生成輔助路徑 - 欄位同步:主欄位變更時自動更新相關輔助欄位
- 動態新增:
addNewLocus()支援新增座標 - 陣列資料結構:所有座標相關欄位皆為陣列
- 索引管理:透過
lociCount管理座標數量
- 自動禁用:輔助欄位通常設為禁用狀態
- 自動同步:主欄位變更時自動更新輔助欄位值
- 視覺區分:使用不同 CSS 類別區分主欄位與輔助欄位
- POST
/run-procedure:提交表單資料,啟動批次處理 - GET
/stream/{batch_id}:SSE 端點,接收處理進度 - GET
/batch-status/{batch_id}:查詢批次執行狀態 - GET
/download/{room_name}:下載處理結果(後端參數名為room_name,通常對應於 batch_id 所建立的結果資料夾)
- GET
/analyze-sequence-stream:SSE 端點,提供即時序列分析進度- 參數:
batch_id,locus,identifier_type,identifier_value - 回傳:Server-Sent Events 格式的分析進度和結果
- 參數:
- GET
/get-available-samples/{batch_id}/{locus}:獲取指定批次和基因座的可用樣本列表- 回傳:JSON 格式的樣本 ID 和條碼 ID 列表
- GET
/health:系統健康檢查端點
// 表單提交
const response = await fetch(`${HOST_URL}run-procedure`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(this.formData)
});- 成功響應:取得
batch_id並啟動 SSE 連接 - 錯誤處理:統一錯誤訊息顯示與日誌記錄
- 狀態碼處理:根據 HTTP 狀態碼進行對應處理
- 在
path-fields.js或locus-fields.js中新增欄位配置 - 在
formData中新增對應的資料屬性 - 如需輔助欄位,配置
secondary屬性 - 更新後端 API 以接收新欄位
- 在對應的 Mixin 中新增方法
- 在元件模板中綁定事件
- 更新 CSS 樣式(如需要)
- 測試深色/淺色模式兼容性
- 日誌緩衝:避免頻繁的 IndexedDB 寫入
- 懶加載:大量日誌時考慮分頁載入
- 記憶體管理:適時清理不需要的日誌資料
- 連接管理:適當處理 SSE 連接的建立與關閉
- Console 日誌:關鍵操作都有 console.log 記錄
- 錯誤捕捉:try-catch 包裝關鍵異步操作
- 狀態追蹤:
connectionState等狀態變數協助除錯 - 全域存取:
window.app提供全域存取方便除錯
| 主題 | 本文件定位 | 相關文件 |
|---|---|---|
| 規則與推薦 | 僅說明前端表單與結果展示 | qc.md, locus_qc.md |
| LLM 詳細流程 | 只描述 modal 觸發 | llm.md |
| 路徑結構 | 僅輸入欄位 | path.md |
| 日誌格式與狀態檔案 | 前端顯示層 | log_flow.md |
| 序列萃取 | 只提供表單欄位 | sequence_extraction.md |