Files
sunhpc-go/pkg/wizard/model.go
2026-02-28 19:29:17 +08:00

322 lines
9.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package wizard
import (
"fmt"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
)
// PageType 页面类型
type PageType int
// 总页码
const TotalPages = 6
// Config 系统配置结构
type Config struct {
// 协议
License string `json:"license"`
AgreementAccepted bool `json:"agreement_accepted"`
// 数据接收
ClusterName string `json:"cluster_name"`
Country string `json:"country"`
Region string `json:"region"`
Timezone string `json:"timezone"`
HomePage string `json:"homepage"`
DBAddress string `json:"db_address"`
Software string `json:"software"`
// 公网设置
PublicInterface string `json:"public_interface"`
PublicIPAddress string `json:"ip_address"`
PublicNetmask string `json:"netmask"`
PublicGateway string `json:"gateway"`
PublicMTU string `json:"public_mtu"`
// 内网配置
PrivateInterface string `json:"private_interface"`
PrivateIPAddress string `json:"private_ip"`
PrivateNetmask string `json:"private_mask"`
PrivateMTU string `json:"private_mtu"`
// DNS 配置
DNSPrimary string `json:"dns_primary"`
DNSSecondary string `json:"dns_secondary"`
}
const (
PageAgreement PageType = iota
PageData
PagePublicNetwork
PageInternalNetwork
PageDNS
PageSummary
)
// model TUI 主模型
type model struct {
config Config // 全局配置
currentPage PageType // 当前页面
totalPages int
textInputs []textinput.Model // 当前页面的输入框
networkInterfaces []string // 所有系统网络接口
width int
height int
err error
quitting bool
done bool
force bool
// 核心1: 按页面分组存储所有组件(6个页面 + 6个map)
pageComponents map[PageType]map[string]Focusable
// 核心2焦点管理器(每次切换页面时重置)
focusManager *FocusManager
}
// defaultConfig 返回默认配置
func defaultConfig() Config {
var (
defaultPublicInterface string
defaultInternalInterface string
)
interfaces := getNetworkInterfaces()
switch len(interfaces) {
case 0:
defaultPublicInterface = ""
defaultInternalInterface = ""
case 1:
defaultPublicInterface = interfaces[0]
defaultInternalInterface = fmt.Sprintf("%s:0", interfaces[0])
case 2:
defaultPublicInterface = interfaces[0]
defaultInternalInterface = interfaces[1]
default:
defaultPublicInterface = interfaces[0]
defaultInternalInterface = interfaces[1]
}
return Config{
License: "This test license is for testing purposes only. Do not use it in production.",
ClusterName: "cluster.hpc.org",
Country: "China",
Region: "Beijing",
Timezone: "Asia/Shanghai",
HomePage: "www.sunhpc.com",
DBAddress: "/var/lib/sunhpc/sunhpc.db",
Software: "/export/sunhpc",
PublicInterface: defaultPublicInterface,
PublicIPAddress: "",
PublicNetmask: "",
PublicGateway: "",
PrivateInterface: defaultInternalInterface,
PrivateIPAddress: "172.16.9.254",
PrivateNetmask: "255.255.255.0",
PrivateMTU: "1500",
DNSPrimary: "8.8.8.8",
DNSSecondary: "8.8.4.4",
}
}
// initialModel 初始化模型
func initialModel() model {
cfg := defaultConfig()
// 1. 初始化所有页面组件(6个页面)
pageComponents := make(map[PageType]map[string]Focusable)
// ------------------ 页面1协议页面 --------------------
page1Comps := make(map[string]Focusable)
page1Comps["accept_btn"] = NewButton("接受协议")
page1Comps["reject_btn"] = NewButton("拒绝协议")
pageComponents[PageAgreement] = page1Comps
// ------------------ 页面2基础信息页面 --------------------
page2Comps := make(map[string]Focusable)
page2Comps["Homepage_input"] = NewTextInput("Homepage", cfg.HomePage)
page2Comps["ClusterName_input"] = NewTextInput("ClusterName", cfg.ClusterName)
page2Comps["Country_input"] = NewTextInput("Country", cfg.Country)
page2Comps["Region_input"] = NewTextInput("Region", cfg.Region)
page2Comps["Timezone_input"] = NewTextInput("Timezone", cfg.Timezone)
page2Comps["DBPath_input"] = NewTextInput("DBPath", cfg.DBAddress)
page2Comps["Software_input"] = NewTextInput("Software", cfg.Software)
page2Comps["next_btn"] = NewButton("下一步")
page2Comps["prev_btn"] = NewButton("上一步")
pageComponents[PageData] = page2Comps
// ------------------ 页面3公网网络页面 --------------------
page3Comps := make(map[string]Focusable)
page3Comps["PublicInterface_input"] = NewTextInput("PublicInterface", cfg.PublicInterface)
page3Comps["PublicIPAddress_input"] = NewTextInput("PublicIPAddress", cfg.PublicIPAddress)
page3Comps["PublicNetmask_input"] = NewTextInput("PublicNetmask", cfg.PublicNetmask)
page3Comps["PublicGateway_input"] = NewTextInput("PublicGateway", cfg.PublicGateway)
page3Comps["PublicMTU_input"] = NewTextInput("PublicMTU", cfg.PublicMTU)
page3Comps["next_btn"] = NewButton("下一步")
page3Comps["prev_btn"] = NewButton("上一步")
pageComponents[PagePublicNetwork] = page3Comps
// ------------------ 页面4内网网络页面 --------------------
page4Comps := make(map[string]Focusable)
page4Comps["PrivateInterface_input"] = NewTextInput("PrivateInterface", cfg.PrivateInterface)
page4Comps["PrivateIPAddress_input"] = NewTextInput("PrivateIPAddress", cfg.PrivateIPAddress)
page4Comps["PrivateNetmask_input"] = NewTextInput("PrivateNetmask", cfg.PrivateNetmask)
page4Comps["PrivateMTU_input"] = NewTextInput("PrivateMTU", cfg.PrivateMTU)
page4Comps["next_btn"] = NewButton("下一步")
page4Comps["prev_btn"] = NewButton("上一步")
pageComponents[PageInternalNetwork] = page4Comps
// ------------------ 页面5DNS页面 --------------------
page5Comps := make(map[string]Focusable)
page5Comps["Pri_DNS_input"] = NewTextInput("Pri DNS", cfg.DNSPrimary)
page5Comps["Sec_DNS_input"] = NewTextInput("Sec DNS", cfg.DNSSecondary)
page5Comps["next_btn"] = NewButton("下一步")
page5Comps["prev_btn"] = NewButton("上一步")
pageComponents[PageDNS] = page5Comps
// ------------------ 页面6Summary页面 --------------------
page6Comps := make(map[string]Focusable)
page6Comps["confirm_btn"] = NewButton("Confirm")
page6Comps["cancel_btn"] = NewButton("Cancel")
pageComponents[PageSummary] = page6Comps
// 创建焦点管理器(初始化聚焦页)
fm := NewFocusManager(true)
// 初始化模型
m := model{
config: cfg,
totalPages: 6,
currentPage: PageAgreement, // 初始化聚焦在协议页面
pageComponents: pageComponents,
focusManager: fm,
}
// 初始化当前页 (页1) 的焦点
m.initPageFocus(m.currentPage)
return m
}
// Init 初始化命令
func (m model) Init() tea.Cmd {
return textinput.Blink
}
// Update 处理消息更新
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c":
m.saveConfig()
//m.quitting = true
return m, tea.Quit
// 1. 焦点切换Tab/Shift+Tab交给管理器处理
case "tab", "shift+tab", "left", "right":
cmd := m.focusManager.HandleInput(msg)
return m, cmd
// 2. 回车键:处理当前焦点组件的点击/确认
case "enter":
currentCompID := m.focusManager.currentFocusID
switch currentCompID {
// 页1accept → 进入页2
case "accept_btn":
return m, m.switchPage(PageData)
// 页1reject → 退出程序
case "reject_btn":
m.quitting = true
return m, tea.Quit
// 通用上一页/下一页逻辑
case "prev_btn":
return m, m.switchPage(m.currentPage - 1)
case "next_btn":
return m, m.switchPage(m.currentPage + 1)
// 页6确认配置 → 退出并保存
case "confirm_btn":
m.done = true
//m.quitting = true
return m, tea.Quit
case "cancel_btn":
m.quitting = true
return m, tea.Quit
}
}
// 其他消息(窗口大小、输入框输入等)...
}
// 处理当前焦点组件的内部更新(比如输入框打字、列表选值)
currentComp, exists := m.focusManager.GetCurrent()
if exists {
// 不同组件的内部更新逻辑(示例)
switch comp := currentComp.(type) {
case *TextInput:
// 输入框更新
newTI, cmd := comp.Model.Update(msg)
comp.Model = newTI
// 保存输入值到全局配置(示例:主机名)
switch m.focusManager.currentFocusID {
// 页2基础信息
case "Homepage_input":
m.config.HomePage = comp.Value()
case "ClusterName_input":
m.config.ClusterName = comp.Value()
case "Country_input":
m.config.Country = comp.Value()
case "Region_input":
m.config.Region = comp.Value()
case "Timezone_input":
m.config.Timezone = comp.Value()
case "DBPath_input":
m.config.DBAddress = comp.Value()
case "Software_input":
m.config.Software = comp.Value()
// 页3公网网络
case "PublicInterface_input":
m.config.PublicInterface = comp.Value()
case "PublicIPAddress_input":
m.config.PublicIPAddress = comp.Value()
case "PublicNetmask_input":
m.config.PublicNetmask = comp.Value()
case "PublicGateway_input":
m.config.PublicGateway = comp.Value()
case "PublicMTU_input":
m.config.PublicMTU = comp.Value()
// 页4内网网络
case "PrivateInterface_input":
m.config.PrivateInterface = comp.Value()
case "PrivateIPAddress_input":
m.config.PrivateIPAddress = comp.Value()
case "PrivateNetmask_input":
m.config.PrivateNetmask = comp.Value()
case "PrivateMTU_input":
m.config.PrivateMTU = comp.Value()
// 页5DNS
case "Pri_DNS_input":
m.config.DNSPrimary = comp.Value()
case "Sec_DNS_input":
m.config.DNSSecondary = comp.Value()
}
return m, cmd
case *List:
// 列表更新
newList, cmd := comp.Model.Update(msg)
comp.Model = newList
return m, cmd
}
}
return m, nil
}