2026-04-29 10:00:40 +00:00
<!DOCTYPE html>
< html lang = "zh-TW" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title > Harmonic CMTS Manager< / title >
2026-05-19 10:25:10 +00:00
< link rel = "icon" type = "image/png" href = "/static/my_icon.png" >
2026-04-29 10:00:40 +00:00
< link rel = "stylesheet" href = "https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" / >
< script src = "https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js" > < / script >
< script src = "https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js" > < / script >
2026-04-30 08:27:18 +00:00
<!-- 引入獨立的 CSS 樣式表 -->
2026-04-29 10:00:40 +00:00
< link rel = "stylesheet" href = "/static/style.css" / >
< / head >
< body >
2026-05-14 07:42:18 +00:00
<!-- 1. 標題加上 id="app - title" -->
< h1 id = "app-title" style = "cursor: default;" > 🎙️ Harmonic CMTS Admin< / h1 >
2026-04-29 10:00:40 +00:00
< div class = "global-settings" >
< label > < strong > 🌐 目標設備:< / strong > < / label >
< input type = "text" id = "cmtsHost" placeholder = "IP 地址" value = "10.14.110.4" style = "width: 130px;" >
< input type = "text" id = "cmtsUser" placeholder = "帳號" value = "admin" style = "width: 100px;" >
< input type = "password" id = "cmtsPass" placeholder = "密碼" value = "nsgadmin" style = "width: 100px;" >
< div style = "margin-left: 15px; display: flex; align-items: center; gap: 10px;" >
2026-05-18 03:51:23 +00:00
< button onclick = "connectWebSocket()" id = "btnConnect" class = "btn-modern btn-connect" > 連線至 CMTS< / button >
< button onclick = "disconnectWebSocket()" id = "btnDisconnect" class = "btn-modern btn-disconnect" style = "display: none;" > 斷開連線< / button >
2026-04-29 10:00:40 +00:00
< span id = "wsStatus" style = "color: #7f8c8d; font-size: 14px; font-weight: bold;" > 狀態:未連線< / span >
< / div >
< / div >
< div class = "tabs" >
< button class = "tab-btn active" onclick = "switchTab('cli-tab', this)" > 💻 True SSH Terminal< / button >
< button class = "tab-btn" onclick = "switchTab('query-tab', this)" > 🔍 CMTS 狀態查詢< / button >
2026-05-27 03:33:01 +00:00
< button class = "tab-btn" onclick = "switchTab('config-tab', this)" > 🛠️ CMTS 設備配置< / button >
2026-04-30 08:27:18 +00:00
<!-- 💡 新增:系統設定頁籤 -->
< button class = "tab-btn" onclick = "switchTab('settings-tab', this); openSystemSettings();" > ⚙️ 系統設定< / button >
2026-05-26 10:30:10 +00:00
<!-- 💡 新增:備份與還原頁籤 -->
< button class = "tab-btn" onclick = "switchTab('backup-tab', this); loadBackupHistory();" > 💾 設備備份與還原< / button >
2026-04-29 10:00:40 +00:00
< / div >
<!-- Tab 1: True SSH Terminal -->
< div id = "cli-tab" class = "tab-content active" >
< div class = "control-group" >
< label > < strong > 快捷指令:< / strong > < / label >
< select id = "fixedCmd" >
2026-05-19 10:25:10 +00:00
< option value = "show cable modem | nomore" > 數據機狀態 (show cable modem | nomore)< / option >
< option value = "show cable rpd | nomore" > RPD 狀態 (show cable rpd | nomore)< / option >
2026-04-29 10:00:40 +00:00
< option value = "show running-config | nomore" > 系統實時設定檔 (show running-config | nomore)< / option >
< / select >
2026-05-15 07:17:28 +00:00
< button onclick = "injectCommand()" class = "btn-modern btn-load" > 送出指令< / button >
2026-04-29 10:00:40 +00:00
< div style = "margin-left: auto; display: flex; gap: 8px;" >
2026-05-15 07:17:28 +00:00
< button onclick = "downloadTerminal()" class = "btn-modern btn-slate" > 💾 下載紀錄< / button >
2026-04-29 10:00:40 +00:00
< / div >
< / div >
< div id = "terminal-container" > < / div >
< / div >
<!-- Tab 2: CMTS 狀態查詢 -->
< div id = "query-tab" class = "tab-content" style = "overflow-y: auto; background-color: #f5f7fa;" >
< div class = "manager-container" >
< div class = "control-group" style = "background: #ffffff; padding: 15px 25px; border-radius: 8px; margin-bottom: 0; box-shadow: 0 2px 4px rgba(0,0,0,0.02); border: 1px solid #e2e8f0;" >
< label style = "font-weight: bold; color: #2c3e50; font-size: 16px; margin-right: 10px;" > 📌 選擇查詢任務:< / label >
< select id = "queryTask" onchange = "switchQueryTask()" style = "width: 400px; max-width: 100%; font-weight: bold; font-size: 15px; padding: 8px; background-color: #f8f9fa;" >
< option value = "form-cm-query" > 🔎 Cable 狀態綜合查詢< / option >
< option value = "form-rpd-query" > 📡 RPD 狀態綜合查詢< / option >
< / select >
< / div >
<!-- 表單 1: Cable 狀態綜合查詢 -->
< div id = "form-cm-query" class = "task-form active" >
< h3 style = "margin-top: 0; font-size: 18px; color: #2980b9; border-bottom: 2px solid #ecf0f1; padding-bottom: 10px; margin-bottom: 20px;" > Cable 狀態綜合查詢 (show cable modem...)< / h3 >
< div class = "form-grid" >
< div class = "form-row" >
< label > 目標設備 (Cable Modem MAC)< / label >
< input type = "text" id = "queryTargetCm" placeholder = "例: 6467.7240.4076 (留白則查詢全體)" >
< / div >
< div class = "form-row" >
< label > 查詢動作 (Action)< / label >
< select id = "queryTypeCm" onchange = "toggleQueryInputs('Cm')" >
< optgroup label = "Cable Modem 查詢 (支援特定目標或全體)" >
< option value = "base" > 基本狀態 (show cable modem)< / option >
< option value = "cpe" > CPE 資訊 (cpe)< / option >
< option value = "cpe_dhcp" > CPE DHCP (cpe dhcp)< / option >
< option value = "cpe_ipv6" > CPE IPv6 (cpe ipv6)< / option >
< option value = "bonding" > 綁定摘要 (bonding)< / option >
< option value = "bonding_ds" > 下行綁定 (bonding downstream)< / option >
< option value = "bonding_us" > 上行綁定 (bonding upstream)< / option >
< option value = "phy" > 實體層狀態 (phy)< / option >
< option value = "verbose" > 詳細資訊 (verbose)< / option >
< option value = "ofdm_profile" > OFDM Profile (ofdm-profile)< / option >
< option value = "ofdma_profile" > OFDMA Profile (ofdma-profile)< / option >
< option value = "dhcp_verbose" > DHCP 詳細資訊 (dhcp verbose)< / option >
< option value = "service_flow" > Service Flow (service-flow)< / option >
< option value = "service_flow_verbose" > Service Flow 詳細 (service-flow verbose)< / option >
< option value = "qos" > QoS 資訊 (qos)< / option >
< option value = "uptime" > 運行時間 (uptime)< / option >
< option value = "ugs" > UGS 資訊 (ugs)< / option >
< option value = "cm_status" > CM 狀態 (cm-status)< / option >
< / optgroup >
< optgroup label = "全域狀態查詢 (不分特定目標)" >
< option value = "partial_mode" > Partial Mode (partial-mode)< / option >
< option value = "hop" > Cable Hop (show cable hop)< / option >
< option value = "flap_list" > Flap List (show cable flap-list)< / option >
< option value = "flap_sum" > Flap Summary (show cable flap-sum)< / option >
< / optgroup >
< / select >
< / div >
< / div >
< div class = "form-actions" >
2026-05-15 07:17:28 +00:00
< button onclick = "executeQuery('Cm')" class = "btn-modern btn-load" > 執行查詢< / button >
2026-04-29 10:00:40 +00:00
< div style = "display: flex; align-items: center;" >
< input type = "checkbox" id = "debugQueryCm" style = "width: 18px; height: 18px; margin: 0; cursor: pointer;" >
< label for = "debugQueryCm" style = "cursor: pointer; color: #7f8c8d; margin-left: 8px; font-size: 14px;" > 🐞 顯示底層指令 (Debug)< / label >
< / div >
< / div >
< / div >
<!-- 表單 2: RPD 狀態綜合查詢 -->
< div id = "form-rpd-query" class = "task-form" >
< h3 style = "margin-top: 0; font-size: 18px; color: #e67e22; border-bottom: 2px solid #ecf0f1; padding-bottom: 10px; margin-bottom: 20px;" > RPD 狀態綜合查詢 (show cable rpd...)< / h3 >
< div class = "form-grid" >
< div class = "form-row" >
< label > 目標設備 (RPD VC:VS)< / label >
< input type = "text" id = "queryTargetRpd" placeholder = "例: 13:0 (留白則查詢全體)" >
< / div >
< div class = "form-row" >
< label > 查詢動作 (Action)< / label >
< select id = "queryTypeRpd" onchange = "toggleQueryInputs('Rpd')" >
< option value = "rpd_base" > 基本狀態 (show cable rpd)< / option >
< option value = "rpd_verbose" > 詳細資訊 (verbose)< / option >
< option value = "rpd_ptp_time" > PTP Time Property (ptp time-property)< / option >
< option value = "rpd_ptp_verbose" > PTP 詳細資訊 (ptp verbose)< / option >
< option value = "rpd_counters_map" > Counters Map (counters map)< / option >
< option value = "rpd_capabilities" > 能力資訊 (capabilities)< / option >
< option value = "rpd_video_counters" > Video Counters (video-channel counters)< / option >
< option value = "rpd_env_temp" > 環境溫度 (environment temperature)< / option >
< option value = "rpd_env_volt" > 環境電壓 (environment voltage)< / option >
< option value = "rpd_session" > Session (session)< / option >
< option value = "rpd_reset_history" > 重啟歷史 (reset-history)< / option >
< option value = "rpd_port_transceiver" > 光模組資訊 (port-transceiver)< / option >
< / select >
< / div >
< / div >
< div class = "form-actions" >
2026-05-15 07:17:28 +00:00
< button onclick = "executeQuery('Rpd')" class = "btn-modern btn-load" > 執行查詢< / button >
2026-04-29 10:00:40 +00:00
< div style = "display: flex; align-items: center;" >
< input type = "checkbox" id = "debugQueryRpd" style = "width: 18px; height: 18px; margin: 0; cursor: pointer;" >
< label for = "debugQueryRpd" style = "cursor: pointer; color: #7f8c8d; margin-left: 8px; font-size: 14px;" > 🐞 顯示底層指令 (Debug)< / label >
< / div >
< / div >
< / div >
2026-04-30 08:27:18 +00:00
< / div >
< / div >
2026-04-29 10:00:40 +00:00
<!-- Tab 3: CMTS 設備配置 -->
< div id = "config-tab" class = "tab-content" style = "overflow-y: auto; background-color: #fdfefe;" >
< div class = "manager-container" >
2026-05-14 07:42:18 +00:00
< div class = "control-group" style = "background: #ffffff; padding: 10px 15px; border-radius: 8px; margin-bottom: 0; box-shadow: 0 2px 4px rgba(0,0,0,0.02); border: 1px solid #e2e8f0;" >
2026-05-27 03:33:01 +00:00
< label style = "font-weight: bold; color: #c0392b; font-size: 16px; margin-right: 10px;" > 📌 選擇配置任務:< / label >
2026-04-30 08:27:18 +00:00
< select id = "configTask" onchange = "switchConfigTask()" style = "width: 400px; max-width: 100%; font-weight: bold; font-size: 15px; padding: 8px; background-color: #fcf3f2; border-color: #fadbd8;" >
2026-05-19 10:25:10 +00:00
<!-- 🌟 拆分為兩個獨立的選項 -->
< option value = "form-running-config" > 🌳 設備配置樹狀圖 (running-config)< / option >
< option value = "form-full-config" > 🌳 完整設備配置樹狀圖 (full configuration)< / option >
2026-04-29 10:00:40 +00:00
< option value = "form-bonding-config" > ⚠️ MAC Domain 狀態感知配置精靈< / option >
< / select >
2026-05-15 07:17:28 +00:00
< button id = "btn-load-task" onclick = "startConfigTask()" class = "btn-modern btn-load" style = "margin-left: 10px;" disabled > 🚀 載入任務< / button >
2026-04-29 10:00:40 +00:00
< / div >
2026-04-30 08:27:18 +00:00
<!-- 表單 3: MAC Domain 配置精靈 -->
< div id = "form-bonding-config" class = "task-form active" >
2026-04-29 10:00:40 +00:00
< h3 style = "margin-top: 0; font-size: 18px; color: #c0392b; border-bottom: 2px solid #ecf0f1; padding-bottom: 10px; margin-bottom: 20px;" > ⚠️ MAC Domain 狀態感知配置精靈< / h3 >
< div class = "control-group" style = "background: #fdf2e9; padding: 15px; border-radius: 6px; border: 1px solid #fadbd8;" >
< label style = "font-weight: bold; color: #d35400;" > 1. 目標 MAC Domain: < / label >
< select id = "cfgMacDomain" style = "width: 150px; padding: 6px; font-weight: bold; border: 1px solid #fadbd8; border-radius: 4px; background-color: #fff;" >
< option value = "" > 請先點擊上方載入任務...< / option >
< / select >
2026-05-15 07:17:28 +00:00
< button onclick = "fetchMacDomainConfig()" class = "btn-modern btn-load" > 🔍 讀取現有配置< / button >
2026-04-29 10:00:40 +00:00
< span id = "fetchStatus" style = "margin-left: 10px; font-size: 14px; color: #7f8c8d;" > 請先讀取設備狀態...< / span >
< / div >
< div id = "macDomainConfigArea" style = "display: none; margin-top: 20px;" >
<!-- Common Settings -->
< h4 style = "color: #2980b9; border-left: 4px solid #2980b9; padding-left: 8px;" > Common Settings< / h4 >
< div class = "form-grid" >
< div class = "form-row" > < label > IP Provisioning Mode< / label > < select id = "g_ip_prov" > < option value = "alternate" > alternate< / option > < option value = "dual-stack" > dual-stack< / option > < option value = "ipv4-only" > ipv4-only< / option > < option value = "ipv6-only" > ipv6-only< / option > < / select > < / div >
< div class = "form-row" > < label > Diplexer Band Edge Control< / label > < select id = "g_diplexer" > < option value = "enabled" > enabled< / option > < option value = "disabled" > disabled< / option > < / select > < / div >
< div class = "form-row" > < label > CM Battery Mode 3.1< / label > < select id = "g_bat31" > < option value = "enabled" > enabled< / option > < option value = "disabled" > disabled< / option > < / select > < / div >
< div class = "form-row" > < label > CM Battery Mode 3.0< / label > < select id = "g_bat30" > < option value = "enabled" > enabled< / option > < option value = "disabled" > disabled< / option > < / select > < / div >
< div class = "form-row" > < label > DOCSIS 4.0< / label > < select id = "g_docsis40" > < option value = "enabled" > enabled< / option > < option value = "disabled" > disabled< / option > < / select > < / div >
< div class = "form-row" > < label > DS Dynamic Bonding Group< / label > < select id = "g_ds_dyn" onchange = "toggleGroupVisibility()" > < option value = "enabled" > enabled< / option > < option value = "disabled" > disabled< / option > < / select > < / div >
< div class = "form-row" > < label > US Dynamic Bonding Group< / label > < select id = "g_us_dyn" onchange = "toggleGroupVisibility()" > < option value = "enabled" > enabled< / option > < option value = "disabled" > disabled< / option > < / select > < / div >
< / div >
<!-- [Basic] DS/US Channel Sets -->
< h4 style = "color: #27ae60; border-left: 4px solid #27ae60; padding-left: 8px; margin-top: 30px;" > [Basic] DS/US Channel Sets< / h4 >
< div class = "form-grid" >
< div class = "form-row" > < label > Admin State< / label > < select id = "b_admin" > < option value = "up" > up< / option > < option value = "down" > down< / option > < / select > < / div >
< div class = "form-row" > < label > DS Primary Set (0..157)< / label > < input type = "text" id = "b_ds_pri" placeholder = "例: 0-2" > < / div >
< div class = "form-row" > < label > DS Non-Primary Set (0..157)< / label > < input type = "text" id = "b_ds_non_pri" placeholder = "例: 3-4" > < / div >
< div class = "form-row" > < label > US PHY Channel Set (0..255)< / label > < input type = "text" id = "b_us_phy" placeholder = "例: 0-3" > < / div >
< div class = "form-row" > < label > DS OFDM Set (0..7)< / label > < input type = "text" id = "b_ds_ofdm" placeholder = "例: 0" > < / div >
< div class = "form-row" > < label > US OFDMA Set (0..1)< / label > < input type = "text" id = "b_us_ofdma" placeholder = "例: 0" > < / div >
< / div >
<!-- [Static] Downstream Bonding Groups -->
< div id = "section_group_ds" style = "margin-top: 30px;" >
< h4 style = "color: #8e44ad; border-left: 4px solid #8e44ad; padding-left: 8px;" > [Static] Downstream Bonding Groups< / h4 >
< div class = "control-group" style = "background: #f4f6f7; padding: 10px; border-radius: 4px;" >
< label > 選擇要編輯的 DS Group: < / label >
< select id = "select_ds_group" onchange = "loadDsGroupData()" style = "width: 200px;" > < / select >
< input type = "text" id = "input_new_ds_group" placeholder = "輸入新 Group 名稱 (例: D4A)" style = "display: none; width: 200px;" >
< / div >
< div class = "form-grid" >
< div class = "form-row" > < label > Admin State< / label > < select id = "ds_g_admin" > < option value = "up" > up< / option > < option value = "down" > down< / option > < / select > < / div >
< div class = "form-row" > < label > Down Channel Set (0..157)< / label > < input type = "text" id = "ds_g_down" placeholder = "例: 0-4" > < / div >
< div class = "form-row" > < label > OFDM Channel Set (0..7)< / label > < input type = "text" id = "ds_g_ofdm" placeholder = "例: 0-1" > < / div >
< div class = "form-row" > < label > FDX OFDM Channel Set (0..7)< / label > < input type = "text" id = "ds_g_fdx" placeholder = "例: 0-2" > < / div >
< / div >
< / div >
<!-- [Static] Upstream Bonding Groups -->
< div id = "section_group_us" style = "margin-top: 30px;" >
< h4 style = "color: #f39c12; border-left: 4px solid #f39c12; padding-left: 8px;" > [Static] Upstream Bonding Groups< / h4 >
< div class = "control-group" style = "background: #f4f6f7; padding: 10px; border-radius: 4px;" >
< label > 選擇要編輯的 US Group: < / label >
< select id = "select_us_group" onchange = "loadUsGroupData()" style = "width: 200px;" > < / select >
< input type = "text" id = "input_new_us_group" placeholder = "輸入新 Group 名稱 (例: U4A)" style = "display: none; width: 200px;" >
< / div >
< div class = "form-grid" >
< div class = "form-row" > < label > Admin State< / label > < select id = "us_g_admin" > < option value = "up" > up< / option > < option value = "down" > down< / option > < / select > < / div >
< div class = "form-row" > < label > US Channel Set< / label > < input type = "text" id = "us_g_us" placeholder = "例: 0-3.0" > < / div >
< div class = "form-row" > < label > OFDMA Channel Set (0..1)< / label > < input type = "text" id = "us_g_ofdma" placeholder = "例: 0" > < / div >
< div class = "form-row" > < label > FDX OFDMA Channel Set (0..5)< / label > < input type = "text" id = "us_g_fdx" placeholder = "例: 0-5" > < / div >
< / div >
< / div >
<!-- CLI 預覽區塊 -->
< div id = "cliPreviewContainer" style = "display: none; background: #2c3e50; color: #ecf0f1; padding: 15px; border-radius: 6px; margin-top: 30px; margin-bottom: 20px; font-family: monospace; white-space: pre-wrap;" > < / div >
< div class = "form-actions" >
2026-05-15 07:17:28 +00:00
< button id = "btnRevert" onclick = "revertFormChanges()" class = "btn-modern btn-disconnect" style = "margin-right: 15px;" > 🔄 復原重填< / button >
< button onclick = "generateMacDomainCLI()" class = "btn-modern btn-scan" > ⚙️ 產生絕對路徑指令預覽< / button >
< button id = "btnDeployConfig" onclick = "executeBondingConfig()" class = "btn-modern btn-save" style = "display: none;" disabled > 🚀 正式套用設定< / button >
2026-04-29 10:00:40 +00:00
< / div >
< / div >
< / div >
2026-04-30 08:27:18 +00:00
2026-05-19 10:25:10 +00:00
<!-- 表單 4: 設備配置樹狀圖 (共用容器) -->
< div id = "form-tree-config" class = "task-form" style = "display: none;" >
2026-05-29 16:42:13 +00:00
<!-- 頂部:標題與按鈕區塊 (維持原樣) -->
2026-05-27 03:33:01 +00:00
< div style = "display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid #ecf0f1; padding-bottom: 10px; margin-bottom: 15px; width: 100%;" >
< div style = "display: flex; align-items: center; gap: 15px;" >
< h3 style = "margin: 0; font-size: 16px; color: #2c3e50;" >
2026-05-29 16:42:13 +00:00
< span id = "tree-form-title" > 🌲 設備配置樹狀圖< / span >
2026-05-27 03:33:01 +00:00
< / h3 >
< span id = "scan-status" style = "color: #7f8c8d; font-size: 14px; font-weight: normal;" > < / span >
< span id = "loading-message" style = "display: none; color: #f39c12; font-size: 14px; font-weight: normal;" >
⏳ 正在從設備抓取完整配置,可能需要 30~60 秒,請耐心稍候...
< / span >
< / div >
< div style = "display: flex; gap: 10px;" >
2026-05-29 16:42:13 +00:00
< button id = "btn-scan-visible" onclick = "scanVisibleMissingOptions()" class = "btn-modern btn-scan" disabled style = "display: none;" > 掃描局部缺失< / button >
< button id = "btn-clear-visible" onclick = "clearVisibleCache()" class = "btn-modern btn-clear" style = "display: none;" > 清除局部快取< / button >
< button id = "btn-scan-global" onclick = "scanGlobalMissingOptions()" class = "btn-modern btn-scan" disabled style = "display: none;" > 掃描全域缺失< / button >
< button id = "btn-clear-global" onclick = "clearGlobalCache()" class = "btn-modern btn-clear" style = "display: none;" > 清除全域快取< / button >
2026-05-27 03:33:01 +00:00
< / div >
< / div >
2026-04-30 10:52:55 +00:00
2026-05-29 16:42:13 +00:00
<!-- 🌟 左右分屏容器 (關鍵修改: align - items: stretch 讓左右強制等高) -->
< div id = "split-container" style = "display: flex; align-items: stretch; width: 100%; position: relative; gap: 10px;" >
2026-05-04 10:26:12 +00:00
2026-05-29 16:42:13 +00:00
<!-- 左側:樹狀圖 (保留 max - height: 600px, 超過才出捲軸) -->
< div id = "tree-container-running" class = "tree-view-instance" style = "flex: 1; min-width: 400px; background-color: #ffffff; padding: 15px; border-radius: 5px; border: 1px solid #e0e0e0; min-height: 100px; max-height: 600px; box-sizing: border-box; overflow-x: auto; overflow-y: auto;" >
2026-05-19 10:25:10 +00:00
< span style = "color: #7f8c8d;" > 尚未載入 Running 資料。請點擊上方「載入任務」按鈕開始。< / span >
< / div >
2026-05-08 08:50:12 +00:00
2026-05-29 16:42:13 +00:00
< div id = "tree-container-full" class = "tree-view-instance" style = "display: none; flex: 1; min-width: 400px; background-color: #ffffff; padding: 15px; border-radius: 5px; border: 1px solid #e0e0e0; min-height: 100px; max-height: 600px; box-sizing: border-box; overflow-x: auto; overflow-y: auto;" >
2026-05-19 10:25:10 +00:00
< span style = "color: #7f8c8d;" > 尚未載入 Full 資料。請點擊上方「載入任務」按鈕開始。< / span >
2026-05-04 10:26:12 +00:00
< / div >
2026-05-29 16:42:13 +00:00
<!-- 🖱️ 拖曳分隔線 (拔除寫死的 height: 400px) -->
< div id = "drag-resizer" style = "display: none; width: 12px; cursor: col-resize; flex-shrink: 0; align-items: center; justify-content: center; z-index: 10;" title = "左右拖曳調整寬度" >
2026-05-04 10:26:12 +00:00
< div style = "width: 4px; height: 40px; background-color: #bdc3c7; border-radius: 2px;" > < / div >
< / div >
2026-04-30 08:27:18 +00:00
2026-05-29 16:42:13 +00:00
<!-- 右側: CLI 預覽與執行結果外框 -->
< div id = "side-cli-preview" style = "flex: 1; min-width: 300px; max-width: 50%; display: none; box-sizing: border-box;" >
2026-05-04 10:26:12 +00:00
2026-05-29 16:42:13 +00:00
<!-- 🌟 內部黑底容器 (使用 flex column 與 height: 100% 完美填滿外框) -->
< div style = "display: flex; flex-direction: column; height: 100%; background: #1e1e1e; padding: 15px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); border: 1px solid #34495e; box-sizing: border-box;" >
2026-05-04 10:26:12 +00:00
2026-05-29 16:42:13 +00:00
<!-- 頂部標題列 -->
< div style = "display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; border-bottom: 1px solid #34495e; padding-bottom: 10px; flex-shrink: 0;" >
< h4 id = "side-pane-title" style = "color: #f1c40f; margin: 0; font-size: 15px;" > ⚠️ 即將寫入的指令< / h4 >
2026-06-01 08:20:34 +00:00
< div style = "display: flex; align-items: center; gap: 20px;" >
2026-05-29 16:42:13 +00:00
< button id = "btn-side-cancel" onclick = "hideSideCLI()" class = "btn-modern btn-disconnect" style = "padding: 5px 12px; font-size: 13px;" > 取消< / button >
< button id = "btn-side-confirm" onclick = "executeSideCLI()" class = "btn-modern btn-save" style = "padding: 5px 12px; font-size: 13px;" > 🚀 確認寫入< / button >
< button id = "btn-side-close" onclick = "hideSideCLI()" class = "btn-modern btn-load" style = "padding: 5px 12px; font-size: 13px; display: none;" > ✅ 完成並關閉< / button >
< / div >
2026-05-04 10:26:12 +00:00
< / div >
2026-05-29 16:42:13 +00:00
<!-- 模式一:指令預覽框 (🌟 拔除 height: 400px, 改用 flex: 1 自動填滿) -->
< textarea id = "side-cli-textarea" style = "flex: 1; width: 100%; background: transparent; color: #ecf0f1; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 14px; line-height: 1.5; padding: 0; border: none; outline: none; box-sizing: border-box; white-space: pre; overflow-x: auto; overflow-y: auto; margin: 0; display: block; resize: none; min-height: 150px;" > < / textarea >
<!-- 模式二:執行結果框 (🌟 拔除 height: 400px, 改用 flex: 1 自動填滿) -->
< div id = "side-execution-result" style = "flex: 1; width: 100%; background: transparent; color: #ecf0f1; font-family: 'Consolas', 'Monaco', 'Courier New', monospace; font-size: 14px; line-height: 1.5; padding: 0; border: none; box-sizing: border-box; overflow-x: auto; overflow-y: auto; margin: 0; display: none; white-space: pre; min-height: 150px;" > < / div >
2026-05-04 10:26:12 +00:00
< / div >
< / div >
2026-04-30 08:27:18 +00:00
< / div >
2026-05-04 10:26:12 +00:00
< / div >
2026-04-29 10:00:40 +00:00
< / div >
< / div >
2026-04-30 08:27:18 +00:00
<!-- 💡 Tab 4: 系統設定 (System Settings) -->
< div id = "settings-tab" class = "tab-content" style = "overflow-y: auto; background-color: #fdfefe; text-align: left;" >
< div class = "manager-container" style = "display: block; max-width: 100%;" >
< div class = "control-group" style = "background: #ffffff; padding: 25px 30px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); border: 1px solid #e2e8f0; text-align: left; display: block;" >
< h3 style = "margin-top: 0; font-size: 18px; color: #2c3e50; border-bottom: 2px solid #ecf0f1; padding-bottom: 10px; margin-bottom: 20px;" >
⚙️ 全域系統設定 (God Mode Filters)
< / h3 >
< div style = "background: #f8f9fa; border-left: 4px solid #3498db; padding: 12px 15px; border-radius: 4px; margin-bottom: 20px;" >
< p style = "color: #2c3e50; font-size: 15px; margin: 0;" >
請勾選您希望在「完整設備配置樹狀圖」中 < b > 隱藏< / b > 的設定項目。< br >
< span style = "color: #7f8c8d; font-size: 14px;" > 💡 點擊下方按鈕載入設備實時配置,您可以深入展開並勾選任何層級的節點進行過濾。< / span >
< / p >
< / div >
2026-05-19 10:25:10 +00:00
<!-- 🌟 新增:過濾器模式切換下拉選單 -->
< div style = "margin-bottom: 15px; display: flex; align-items: center; gap: 10px;" >
< label style = "font-weight: bold; color: #2c3e50;" > 🎯 選擇要編輯的過濾器:< / label >
< select id = "filter-mode-select" onchange = "switchFilterMode()" style = "padding: 6px 10px; border-radius: 4px; border: 1px solid #bdc3c7; font-weight: bold; font-size: 14px; background-color: #fcf3f2; color: #c0392b;" >
< option value = "running" > Running 配置過濾器< / option >
< option value = "full" > Full 配置過濾器< / option >
< / select >
2026-05-27 03:33:01 +00:00
< button onclick = "loadRealConfigForFilters()" class = "btn-modern btn-load" >
2026-05-19 10:25:10 +00:00
📥 載入設備配置以設定過濾
< / button >
< span id = "filter-loading-msg" style = "display: none; color: #e67e22; margin-left: 10px; font-weight: bold;" > ⏳ 正在抓取配置,請稍候...< / span >
< / div >
2026-04-30 08:27:18 +00:00
<!-- 樹狀 Checkbox 容器 -->
< div id = "filter-checkboxes" style = "margin: 15px 0; border: 1px solid #bdc3c7; padding: 15px; border-radius: 5px; background-color: #ffffff; overflow-x: auto; max-height: 500px; overflow-y: auto; display: none;" >
< / div >
< div style = "margin-top: 20px; border-top: 1px solid #ecf0f1; padding-top: 20px;" >
2026-05-27 03:33:01 +00:00
< button onclick = "saveSystemSettings()" class = "btn-modern btn-connect" style = "padding: 10px 20px; font-size: 15px;" >
2026-04-30 08:27:18 +00:00
💾 儲存並套用設定
< / button >
< span id = "settings-status" style = "margin-left: 15px; color: #27ae60; font-weight: bold; display: none;" > ✅ 設定已成功儲存!< / span >
< / div >
< / div >
< / div >
< / div >
2026-05-26 10:30:10 +00:00
<!-- 💡 Tab 5: 設備備份與還原 -->
< div id = "backup-tab" class = "tab-content" style = "overflow-y: auto; background-color: #f5f7fa;" >
< div class = "manager-container" >
<!-- 上半部:建立新快照 -->
2026-05-28 14:24:57 +00:00
< div class = "control-group" style = "background: #ffffff; padding: 25px 30px; border-radius: 8px; margin-bottom: 15px; box-shadow: 0 2px 4px rgba(0,0,0,0.02); border: 1px solid #e2e8f0; display: block;" >
<!-- 💡 修正:移除負 margin, 讓底線與內容邊界對齊 (如同系統設定頁籤) -->
< div style = "display: flex; justify-content: flex-start; align-items: center; gap: 15px; border-bottom: 2px solid #ecf0f1; padding-bottom: 12px; margin-bottom: 20px;" >
< h3 style = "margin: 0; font-size: 18px; color: #2c3e50; display: flex; align-items: center; gap: 8px;" >
📸 建立新快照
< / h3 >
< button onclick = "createSnapshot()" id = "btnCreateSnapshot" class = "btn-modern btn-save" style = "padding: 8px 20px; font-size: 14px; background-color: #2980b9; border: none; box-shadow: 0 2px 4px rgba(41, 128, 185, 0.3);" >
🚀 立即執行備份
< / button >
< span id = "backup-status-msg" style = "color: #e67e22; font-size: 14px; display: none; font-weight: bold;" >
⏳ 正在連線設備並抓取設定,請稍候...
< / span >
< / div >
<!-- 表單網格區 -->
< div style = "display: grid; grid-template-columns: 2fr 1fr; gap: 20px; margin-bottom: 15px;" >
< div >
< label style = "display: block; font-size: 14px; font-weight: bold; color: #34495e; margin-bottom: 8px;" >
快照名稱 < span style = "color: #e74c3c;" > *< / span >
< / label >
< input type = "text" id = "snapshotName" placeholder = "例如: 例行備份、升級前備份" style = "width: 100%; padding: 10px 12px; border: 1px solid #bdc3c7; border-radius: 5px; box-sizing: border-box; font-size: 14px; transition: border-color 0.2s;" >
< / div >
< div >
< label style = "display: block; font-size: 14px; font-weight: bold; color: #34495e; margin-bottom: 8px;" >
配置類型
< / label >
< select id = "backupConfigType" style = "width: 100%; padding: 10px 12px; border: 1px solid #bdc3c7; border-radius: 5px; box-sizing: border-box; font-size: 14px; background-color: #f8f9fa; cursor: pointer;" >
< option value = "running" > Running Config (運行中配置)< / option >
< option value = "full" > Full Config (完整配置)< / option >
< / select >
< / div >
< / div >
<!-- 描述區 -->
< div style = "margin-bottom: 0;" >
< label style = "display: block; font-size: 14px; font-weight: bold; color: #34495e; margin-bottom: 8px;" >
備份描述 < span style = "color: #7f8c8d; font-weight: normal; font-size: 13px;" > (選填)< / span >
< / label >
< input type = "text" id = "snapshotDescription" placeholder = "請簡述此次備份的目的,例如:升級至 Unify FDD firmware, 驗收通過" style = "width: 100%; padding: 10px 12px; border: 1px solid #bdc3c7; border-radius: 5px; box-sizing: border-box; font-size: 14px;" >
2026-05-26 10:30:10 +00:00
< / div >
< / div >
<!-- 下半部:歷史紀錄列表 -->
2026-05-28 14:24:57 +00:00
< div class = "control-group" style = "background: #ffffff; padding: 25px 30px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.02); border: 1px solid #e2e8f0; display: block;" >
2026-05-27 03:33:01 +00:00
2026-05-28 14:24:57 +00:00
<!-- 💡 修正:移除負 margin, 讓底線與內容邊界對齊 -->
< div style = "display: flex; align-items: center; gap: 20px; border-bottom: 2px solid #ecf0f1; padding-bottom: 12px; margin-bottom: 20px; flex-wrap: wrap;" >
< h3 style = "margin: 0; font-size: 18px; color: #2c3e50; display: flex; align-items: center; gap: 8px; white-space: nowrap;" >
📁 歷史備份紀錄
2026-05-27 03:33:01 +00:00
< / h3 >
2026-05-28 14:24:57 +00:00
< div style = "display: flex; gap: 10px; align-items: center;" >
< input type = "text" id = "filter-keyword" placeholder = "🔍 搜尋名稱或描述..." class = "edit-input" style = "width: 180px; padding: 6px 10px; border: 1px solid #bdc3c7; border-radius: 4px; font-size: 13px;" onkeyup = "applyBackupFilters()" >
< select id = "filter-type" class = "edit-input" style = "width: 110px; padding: 6px 10px; border: 1px solid #bdc3c7; border-radius: 4px; font-size: 13px;" onchange = "applyBackupFilters()" >
< option value = "" > 所有類型< / option >
< option value = "running" > running< / option >
< option value = "full" > full< / option >
< / select >
<!-- 💡 修正:回歸原生 type="date",交由系統決定語系顯示 -->
< div style = "display: flex; align-items: center; gap: 5px; border-left: 1px solid #bdc3c7; padding-left: 10px; margin-left: 2px;" >
< input type = "date" id = "filter-date-start" class = "edit-input" style = "padding: 5px 8px; border: 1px solid #bdc3c7; border-radius: 4px; font-size: 13px; color: #7f8c8d;" onchange = "applyBackupFilters()" >
< span style = "color: #7f8c8d; font-size: 13px;" > 至< / span >
< input type = "date" id = "filter-date-end" class = "edit-input" style = "padding: 5px 8px; border: 1px solid #bdc3c7; border-radius: 4px; font-size: 13px; color: #7f8c8d;" onchange = "applyBackupFilters()" >
< / div >
< button onclick = "loadBackupHistory()" class = "btn-modern btn-slate" style = "margin-left: 5px; padding: 8px 20px; font-size: 14px;" >
🔄 重新整理
< / button >
< / div >
2026-05-26 10:30:10 +00:00
< / div >
2026-05-28 14:24:57 +00:00
<!-- 表格區塊 -->
2026-05-26 10:30:10 +00:00
< table style = "width: 100%; border-collapse: collapse; text-align: left;" >
< thead >
< tr style = "background-color: #f8f9fa; border-bottom: 2px solid #bdc3c7;" >
2026-05-28 14:24:57 +00:00
< th style = "padding: 10px; color: #34495e; width: 20%;" > 時間< / th >
< th style = "padding: 10px; color: #34495e; width: 25%;" > 快照名稱< / th >
< th style = "padding: 10px; color: #34495e; width: 25%;" > 描述< / th >
< th style = "padding: 10px; color: #34495e; width: 10%;" > 類型< / th >
< th style = "padding: 10px; color: #34495e; text-align: right; width: 20%;" > 操作< / th >
2026-05-26 10:30:10 +00:00
< / tr >
< / thead >
< tbody id = "backup-history-tbody" >
< tr >
2026-05-28 14:24:57 +00:00
< td colspan = "5" style = "padding: 20px; text-align: center; color: #7f8c8d;" > 請點擊「重新整理」載入歷史紀錄...< / td >
2026-05-26 10:30:10 +00:00
< / tr >
< / tbody >
< / table >
< / div >
< / div >
< / div >
2026-04-29 10:00:40 +00:00
<!-- 獨立的彈出式輸出視窗 (Modal) -->
2026-06-01 08:20:34 +00:00
< div id = "outputModal" class = "modal-overlay" >
< div class = "modal-container" >
2026-04-29 10:00:40 +00:00
< div class = "modal-header" >
2026-06-01 08:20:34 +00:00
< div class = "modal-title" style = "flex-grow: 1; display: flex; align-items: center;" >
2026-04-29 10:00:40 +00:00
< span > 📄 執行結果< / span >
2026-06-01 08:20:34 +00:00
< span id = "modalTargetInfo" style = "font-size: 13px; color: #bdc3c7; font-weight: normal; margin-left: 10px; flex-grow: 1;" > < / span >
< / div >
< div id = "modal-action-container" style = "display: flex; gap: 20px; align-items: center;" >
< button id = "btn-modal-close" class = "btn-modern btn-disconnect" onclick = "closeModal()" > 關閉視窗< / button >
2026-04-29 10:00:40 +00:00
< / div >
< / div >
< div class = "modal-body" >
< pre id = "modalOutput" class = "readonly-terminal" > 等待執行中...< / pre >
< / div >
< / div >
< / div >
2026-04-30 08:27:18 +00:00
<!-- 引入獨立的 JavaScript 邏輯 -->
2026-05-13 03:09:19 +00:00
< script type = "module" src = "/static/app.js" > < / script >
2026-04-30 08:27:18 +00:00
2026-04-29 10:00:40 +00:00
< / body >
< / html >