Files
sunhpc-go/pkg/wizard/model.go

322 lines
9.8 KiB
Go
Raw Normal View History

2026-02-21 20:22:21 +08:00
package wizard
import (
2026-02-22 20:18:12 +08:00
"fmt"
2026-02-21 20:22:21 +08:00
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
)
2026-02-27 22:52:15 +08:00
// PageType 页面类型
type PageType int
// 总页码
const TotalPages = 6
2026-02-21 20:22:21 +08:00
// Config 系统配置结构
type Config struct {
// 协议
2026-02-27 22:52:15 +08:00
License string `json:"license"`
AgreementAccepted bool `json:"agreement_accepted"`
2026-02-21 20:22:21 +08:00
// 数据接收
2026-02-28 19:29:17 +08:00
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"`
2026-02-21 20:22:21 +08:00
// 公网设置
PublicInterface string `json:"public_interface"`
2026-02-22 20:18:12 +08:00
PublicIPAddress string `json:"ip_address"`
PublicNetmask string `json:"netmask"`
PublicGateway string `json:"gateway"`
2026-02-28 19:29:17 +08:00
PublicMTU string `json:"public_mtu"`
2026-02-21 20:22:21 +08:00
// 内网配置
2026-02-28 19:29:17 +08:00
PrivateInterface string `json:"private_interface"`
PrivateIPAddress string `json:"private_ip"`
PrivateNetmask string `json:"private_mask"`
PrivateMTU string `json:"private_mtu"`
2026-02-21 20:22:21 +08:00
// 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 {
2026-02-27 22:52:15 +08:00
config Config // 全局配置
currentPage PageType // 当前页面
2026-02-22 20:18:12 +08:00
totalPages int
2026-02-27 22:52:15 +08:00
textInputs []textinput.Model // 当前页面的输入框
networkInterfaces []string // 所有系统网络接口
2026-02-22 20:18:12 +08:00
width int
height int
err error
quitting bool
done bool
force bool
2026-02-27 22:52:15 +08:00
// 核心1: 按页面分组存储所有组件(6个页面 + 6个map)
pageComponents map[PageType]map[string]Focusable
// 核心2焦点管理器(每次切换页面时重置)
focusManager *FocusManager
2026-02-21 20:22:21 +08:00
}
// defaultConfig 返回默认配置
func defaultConfig() Config {
2026-02-22 20:18:12 +08:00
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]
}
2026-02-21 20:22:21 +08:00
return Config{
2026-02-28 19:29:17 +08:00
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",
2026-02-21 20:22:21 +08:00
}
}
// initialModel 初始化模型
func initialModel() model {
cfg := defaultConfig()
2026-02-27 22:52:15 +08:00
// 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)
2026-02-28 19:29:17 +08:00
page2Comps["ClusterName_input"] = NewTextInput("ClusterName", cfg.ClusterName)
2026-02-27 22:52:15 +08:00
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)
2026-02-28 19:29:17 +08:00
page3Comps["PublicMTU_input"] = NewTextInput("PublicMTU", cfg.PublicMTU)
2026-02-27 22:52:15 +08:00
page3Comps["next_btn"] = NewButton("下一步")
page3Comps["prev_btn"] = NewButton("上一步")
pageComponents[PagePublicNetwork] = page3Comps
// ------------------ 页面4内网网络页面 --------------------
page4Comps := make(map[string]Focusable)
2026-02-28 19:29:17 +08:00
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)
2026-02-27 22:52:15 +08:00
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)
// 初始化模型
2026-02-21 20:22:21 +08:00
m := model{
2026-02-27 22:52:15 +08:00
config: cfg,
totalPages: 6,
currentPage: PageAgreement, // 初始化聚焦在协议页面
pageComponents: pageComponents,
focusManager: fm,
2026-02-21 20:22:21 +08:00
}
2026-02-27 22:52:15 +08:00
// 初始化当前页 (页1) 的焦点
m.initPageFocus(m.currentPage)
2026-02-21 20:22:21 +08:00
return m
}
// Init 初始化命令
func (m model) Init() tea.Cmd {
return textinput.Blink
}
// Update 处理消息更新
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
2026-02-28 19:29:17 +08:00
2026-02-21 20:22:21 +08:00
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c":
2026-02-28 19:29:17 +08:00
m.saveConfig()
//m.quitting = true
2026-02-21 20:22:21 +08:00
return m, tea.Quit
2026-02-27 22:52:15 +08:00
// 1. 焦点切换Tab/Shift+Tab交给管理器处理
case "tab", "shift+tab", "left", "right":
cmd := m.focusManager.HandleInput(msg)
return m, cmd
2026-02-21 20:22:21 +08:00
2026-02-27 22:52:15 +08:00
// 2. 回车键:处理当前焦点组件的点击/确认
2026-02-21 20:22:21 +08:00
case "enter":
2026-02-28 19:29:17 +08:00
2026-02-27 22:52:15 +08:00
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
2026-02-28 19:29:17 +08:00
//m.quitting = true
2026-02-27 22:52:15 +08:00
return m, tea.Quit
case "cancel_btn":
m.quitting = true
return m, tea.Quit
2026-02-21 20:22:21 +08:00
}
}
2026-02-27 22:52:15 +08:00
// 其他消息(窗口大小、输入框输入等)...
2026-02-21 20:22:21 +08:00
}
2026-02-27 22:52:15 +08:00
// 处理当前焦点组件的内部更新(比如输入框打字、列表选值)
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()
2026-02-28 19:29:17 +08:00
case "ClusterName_input":
m.config.ClusterName = comp.Value()
2026-02-27 22:52:15 +08:00
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()
2026-02-28 19:29:17 +08:00
case "PublicMTU_input":
m.config.PublicMTU = comp.Value()
2026-02-27 22:52:15 +08:00
// 页4内网网络
2026-02-28 19:29:17 +08:00
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()
2026-02-27 22:52:15 +08:00
// 页5DNS
case "Pri_DNS_input":
m.config.DNSPrimary = comp.Value()
case "Sec_DNS_input":
m.config.DNSSecondary = comp.Value()
2026-02-21 20:22:21 +08:00
}
2026-02-27 22:52:15 +08:00
return m, cmd
2026-02-21 20:22:21 +08:00
2026-02-27 22:52:15 +08:00
case *List:
// 列表更新
newList, cmd := comp.Model.Update(msg)
comp.Model = newList
return m, cmd
2026-02-21 20:22:21 +08:00
}
}
return m, nil
}