optimize tui interface and interaction
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
sunhpc
|
sunhpc
|
||||||
|
testgui
|
||||||
|
|||||||
66
cmd/test/main.go
Normal file
66
cmd/test/main.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
)
|
||||||
|
|
||||||
|
// model 定义应用的状态
|
||||||
|
type model struct {
|
||||||
|
items []string // 列表数据
|
||||||
|
selectedIdx int // 当前选中的索引
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init 初始化模型,返回初始命令(这里不需要,返回 nil)
|
||||||
|
func (m model) Init() tea.Cmd {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update 处理用户输入和状态更新
|
||||||
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
// 处理键盘输入
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
// Tab 键:切换选中项(循环切换)
|
||||||
|
case "tab":
|
||||||
|
m.selectedIdx = (m.selectedIdx + 1) % len(m.items)
|
||||||
|
// Ctrl+C 或 q 键:退出程序
|
||||||
|
case "ctrl+c", "q":
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// View 渲染界面
|
||||||
|
func (m model) View() string {
|
||||||
|
s := "网络接口列表(按 Tab 切换选择,按 q 退出)\n\n"
|
||||||
|
|
||||||
|
// 遍历列表项,渲染每一项
|
||||||
|
for i, item := range m.items {
|
||||||
|
// 标记当前选中的项
|
||||||
|
if i == m.selectedIdx {
|
||||||
|
s += fmt.Sprintf("→ %s (选中)\n", item)
|
||||||
|
} else {
|
||||||
|
s += fmt.Sprintf(" %s\n", item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 初始化模型,设置列表数据
|
||||||
|
initialModel := model{
|
||||||
|
items: []string{"eth0", "eth1", "eth2", "eth3"},
|
||||||
|
selectedIdx: 0, // 默认选中第一个项
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动 Bubble Tea 程序
|
||||||
|
p := tea.NewProgram(initialModel)
|
||||||
|
if _, err := p.Run(); err != nil {
|
||||||
|
fmt.Printf("程序运行出错: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,3 +31,8 @@ func GenerateID() (string, error) {
|
|||||||
func GetTimestamp() string {
|
func GetTimestamp() string {
|
||||||
return time.Now().Format("2006-01-02 15:04:05")
|
return time.Now().Format("2006-01-02 15:04:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 定义短语
|
||||||
|
const (
|
||||||
|
NoAvailableNetworkInterfaces = "No available network interfaces"
|
||||||
|
)
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ package wizard
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sunhpc/pkg/utils"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
)
|
)
|
||||||
@@ -66,31 +68,30 @@ func (m *model) saveCurrentPage() {
|
|||||||
|
|
||||||
func (m *model) saveDataPage() {
|
func (m *model) saveDataPage() {
|
||||||
if len(m.textInputs) >= 8 {
|
if len(m.textInputs) >= 8 {
|
||||||
m.config.Hostname = m.textInputs[0].Value()
|
m.config.HomePage = m.textInputs[0].Value()
|
||||||
m.config.Country = m.textInputs[1].Value()
|
m.config.Hostname = m.textInputs[1].Value()
|
||||||
m.config.Region = m.textInputs[2].Value()
|
m.config.Country = m.textInputs[2].Value()
|
||||||
m.config.Timezone = m.textInputs[3].Value()
|
m.config.Region = m.textInputs[3].Value()
|
||||||
m.config.HomePage = m.textInputs[4].Value()
|
m.config.Timezone = m.textInputs[4].Value()
|
||||||
m.config.DBAddress = m.textInputs[5].Value()
|
m.config.DBAddress = m.textInputs[5].Value()
|
||||||
m.config.DBName = m.textInputs[6].Value()
|
m.config.DataAddress = m.textInputs[6].Value()
|
||||||
m.config.DataAddress = m.textInputs[7].Value()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *model) savePublicNetworkPage() {
|
func (m *model) savePublicNetworkPage() {
|
||||||
if len(m.textInputs) >= 4 {
|
if len(m.textInputs) >= 4 {
|
||||||
m.config.PublicInterface = m.textInputs[0].Value()
|
m.config.PublicInterface = m.textInputs[0].Value()
|
||||||
m.config.IPAddress = m.textInputs[1].Value()
|
m.config.PublicIPAddress = m.textInputs[1].Value()
|
||||||
m.config.Netmask = m.textInputs[2].Value()
|
m.config.PublicNetmask = m.textInputs[2].Value()
|
||||||
m.config.Gateway = m.textInputs[3].Value()
|
m.config.PublicGateway = m.textInputs[3].Value()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *model) saveInternalNetworkPage() {
|
func (m *model) saveInternalNetworkPage() {
|
||||||
if len(m.textInputs) >= 3 {
|
if len(m.textInputs) >= 3 {
|
||||||
m.config.InternalInterface = m.textInputs[0].Value()
|
m.config.InternalInterface = m.textInputs[0].Value()
|
||||||
m.config.InternalIP = m.textInputs[1].Value()
|
m.config.InternalIPAddress = m.textInputs[1].Value()
|
||||||
m.config.InternalMask = m.textInputs[2].Value()
|
m.config.InternalNetmask = m.textInputs[2].Value()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,20 +109,18 @@ func (m *model) initPageInputs() {
|
|||||||
switch m.currentPage {
|
switch m.currentPage {
|
||||||
case PageData:
|
case PageData:
|
||||||
fields := []struct{ label, value string }{
|
fields := []struct{ label, value string }{
|
||||||
{"主机名:", m.config.Hostname},
|
{"Homepage", m.config.HomePage},
|
||||||
{"国家:", m.config.Country},
|
{"Hostname", m.config.Hostname},
|
||||||
{"地区:", m.config.Region},
|
{"Country", m.config.Country},
|
||||||
{"时区:", m.config.Timezone},
|
{"Region", m.config.Region},
|
||||||
{"主页:", m.config.HomePage},
|
{"Timezone", m.config.Timezone},
|
||||||
{"数据库地址:", m.config.DBAddress},
|
{"DB Path", m.config.DBAddress},
|
||||||
{"数据库名称:", m.config.DBName},
|
{"Software", m.config.DataAddress},
|
||||||
{"Data 地址:", m.config.DataAddress},
|
|
||||||
}
|
}
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
ti := textinput.New()
|
ti := textinput.New()
|
||||||
ti.Placeholder = ""
|
ti.Placeholder = ""
|
||||||
ti.Placeholder = f.label
|
ti.Placeholder = f.label
|
||||||
//ti.Placeholder = "请输入" + f.label[:len(f.label)-1]
|
|
||||||
ti.SetValue(f.value)
|
ti.SetValue(f.value)
|
||||||
ti.Width = 50
|
ti.Width = 50
|
||||||
m.textInputs = append(m.textInputs, ti)
|
m.textInputs = append(m.textInputs, ti)
|
||||||
@@ -130,18 +129,19 @@ func (m *model) initPageInputs() {
|
|||||||
if len(m.textInputs) > 0 {
|
if len(m.textInputs) > 0 {
|
||||||
m.textInputs[0].Focus()
|
m.textInputs[0].Focus()
|
||||||
}
|
}
|
||||||
m.inputLabels = []string{"Hostname", "Country", "Region", "Timezone", "Homepage", "DBPath", "DBName", "Software"}
|
m.inputLabels = []string{"Homepage", "Hostname", "Country", "Region", "Timezone", "DBPath", "Software"}
|
||||||
|
|
||||||
case PagePublicNetwork:
|
case PagePublicNetwork:
|
||||||
fields := []struct{ label, value string }{
|
fields := []struct{ label, value string }{
|
||||||
{"公网接口:", m.config.PublicInterface},
|
{"Public Interface", m.config.PublicInterface},
|
||||||
{"IP 地址:", m.config.IPAddress},
|
{"Public IP Address", m.config.PublicIPAddress},
|
||||||
{"子网掩码:", m.config.Netmask},
|
{"Public Netmask", m.config.PublicNetmask},
|
||||||
{"网关:", m.config.Gateway},
|
{"Public Gateway", m.config.PublicGateway},
|
||||||
}
|
}
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
ti := textinput.New()
|
ti := textinput.New()
|
||||||
ti.Placeholder = ""
|
ti.Placeholder = ""
|
||||||
|
ti.Placeholder = f.label
|
||||||
ti.SetValue(f.value)
|
ti.SetValue(f.value)
|
||||||
ti.Width = 50
|
ti.Width = 50
|
||||||
m.textInputs = append(m.textInputs, ti)
|
m.textInputs = append(m.textInputs, ti)
|
||||||
@@ -150,17 +150,17 @@ func (m *model) initPageInputs() {
|
|||||||
if len(m.textInputs) > 0 {
|
if len(m.textInputs) > 0 {
|
||||||
m.textInputs[0].Focus()
|
m.textInputs[0].Focus()
|
||||||
}
|
}
|
||||||
m.inputLabels = []string{"Wan iface", "IPAddress", "Netmask", "Gateway"}
|
m.inputLabels = []string{"Public Interface", "Public IP Address", "Public Netmask", "Public Gateway"}
|
||||||
|
|
||||||
case PageInternalNetwork:
|
case PageInternalNetwork:
|
||||||
fields := []struct{ label, value string }{
|
fields := []struct{ label, value string }{
|
||||||
{"内网接口:", m.config.InternalInterface},
|
{"Internal Interface", m.config.InternalInterface},
|
||||||
{"内网 IP:", m.config.InternalIP},
|
{"Internal IP Address", m.config.InternalIPAddress},
|
||||||
{"内网掩码:", m.config.InternalMask},
|
{"Internal Netmask", m.config.InternalNetmask},
|
||||||
}
|
}
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
ti := textinput.New()
|
ti := textinput.New()
|
||||||
ti.Placeholder = ""
|
ti.Placeholder = f.label
|
||||||
ti.SetValue(f.value)
|
ti.SetValue(f.value)
|
||||||
ti.Width = 50
|
ti.Width = 50
|
||||||
m.textInputs = append(m.textInputs, ti)
|
m.textInputs = append(m.textInputs, ti)
|
||||||
@@ -169,16 +169,16 @@ func (m *model) initPageInputs() {
|
|||||||
if len(m.textInputs) > 0 {
|
if len(m.textInputs) > 0 {
|
||||||
m.textInputs[0].Focus()
|
m.textInputs[0].Focus()
|
||||||
}
|
}
|
||||||
m.inputLabels = []string{"Lan iface", "IPAddress", "Netmask"}
|
m.inputLabels = []string{"Internal Interface", "Internal IP", "Internal Mask"}
|
||||||
|
|
||||||
case PageDNS:
|
case PageDNS:
|
||||||
fields := []struct{ label, value string }{
|
fields := []struct{ label, value string }{
|
||||||
{"主 DNS:", m.config.DNSPrimary},
|
{"Primary DNS", m.config.DNSPrimary},
|
||||||
{"备 DNS:", m.config.DNSSecondary},
|
{"Secondary DNS", m.config.DNSSecondary},
|
||||||
}
|
}
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
ti := textinput.New()
|
ti := textinput.New()
|
||||||
ti.Placeholder = ""
|
ti.Placeholder = f.label
|
||||||
ti.SetValue(f.value)
|
ti.SetValue(f.value)
|
||||||
ti.Width = 50
|
ti.Width = 50
|
||||||
m.textInputs = append(m.textInputs, ti)
|
m.textInputs = append(m.textInputs, ti)
|
||||||
@@ -187,6 +187,31 @@ func (m *model) initPageInputs() {
|
|||||||
if len(m.textInputs) > 0 {
|
if len(m.textInputs) > 0 {
|
||||||
m.textInputs[0].Focus()
|
m.textInputs[0].Focus()
|
||||||
}
|
}
|
||||||
m.inputLabels = []string{"DNSPrimary", "DNSSecondary"}
|
m.inputLabels = []string{"Pri DNS", "Sec DNS"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取系统网络接口
|
||||||
|
func getNetworkInterfaces() []string {
|
||||||
|
// 实现获取系统网络接口的逻辑
|
||||||
|
// 例如:使用 net.Interface() 函数获取系统网络接口
|
||||||
|
// 返回一个字符串切片,包含系统网络接口的名称
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return []string{utils.NoAvailableNetworkInterfaces}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []string
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
// 跳过 loopback 接口
|
||||||
|
if iface.Flags&net.FlagLoopback != 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, iface.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result) == 0 {
|
||||||
|
return []string{utils.NoAvailableNetworkInterfaces}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package wizard
|
package wizard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
@@ -17,19 +19,18 @@ type Config struct {
|
|||||||
Timezone string `json:"timezone"`
|
Timezone string `json:"timezone"`
|
||||||
HomePage string `json:"homepage"`
|
HomePage string `json:"homepage"`
|
||||||
DBAddress string `json:"db_address"`
|
DBAddress string `json:"db_address"`
|
||||||
DBName string `json:"db_name"`
|
|
||||||
DataAddress string `json:"data_address"`
|
DataAddress string `json:"data_address"`
|
||||||
|
|
||||||
// 公网设置
|
// 公网设置
|
||||||
PublicInterface string `json:"public_interface"`
|
PublicInterface string `json:"public_interface"`
|
||||||
IPAddress string `json:"ip_address"`
|
PublicIPAddress string `json:"ip_address"`
|
||||||
Netmask string `json:"netmask"`
|
PublicNetmask string `json:"netmask"`
|
||||||
Gateway string `json:"gateway"`
|
PublicGateway string `json:"gateway"`
|
||||||
|
|
||||||
// 内网配置
|
// 内网配置
|
||||||
InternalInterface string `json:"internal_interface"`
|
InternalInterface string `json:"internal_interface"`
|
||||||
InternalIP string `json:"internal_ip"`
|
InternalIPAddress string `json:"internal_ip"`
|
||||||
InternalMask string `json:"internal_mask"`
|
InternalNetmask string `json:"internal_mask"`
|
||||||
|
|
||||||
// DNS 配置
|
// DNS 配置
|
||||||
DNSPrimary string `json:"dns_primary"`
|
DNSPrimary string `json:"dns_primary"`
|
||||||
@@ -59,6 +60,7 @@ type model struct {
|
|||||||
config Config
|
config Config
|
||||||
currentPage PageType
|
currentPage PageType
|
||||||
totalPages int
|
totalPages int
|
||||||
|
networkInterfaces []string // 所有系统网络接口
|
||||||
textInputs []textinput.Model
|
textInputs []textinput.Model
|
||||||
inputLabels []string // 存储标签
|
inputLabels []string // 存储标签
|
||||||
focusIndex int
|
focusIndex int
|
||||||
@@ -74,22 +76,41 @@ type model struct {
|
|||||||
|
|
||||||
// defaultConfig 返回默认配置
|
// defaultConfig 返回默认配置
|
||||||
func defaultConfig() Config {
|
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{
|
return Config{
|
||||||
Hostname: "sunhpc01",
|
Hostname: "cluster.hpc.org",
|
||||||
Country: "China",
|
Country: "China",
|
||||||
Region: "Beijing",
|
Region: "Beijing",
|
||||||
Timezone: "Asia/Shanghai",
|
Timezone: "Asia/Shanghai",
|
||||||
HomePage: "https://sunhpc.example.com",
|
HomePage: "www.sunhpc.com",
|
||||||
DBAddress: "127.0.0.1",
|
DBAddress: "/var/lib/sunhpc/sunhpc.db",
|
||||||
DBName: "sunhpc_db",
|
DataAddress: "/export/sunhpc",
|
||||||
DataAddress: "/data/sunhpc",
|
PublicInterface: defaultPublicInterface,
|
||||||
PublicInterface: "eth0",
|
PublicIPAddress: "",
|
||||||
InternalInterface: "eth1",
|
PublicNetmask: "",
|
||||||
IPAddress: "192.168.1.100",
|
PublicGateway: "",
|
||||||
Netmask: "255.255.255.0",
|
InternalInterface: defaultInternalInterface,
|
||||||
Gateway: "192.168.1.1",
|
InternalIPAddress: "172.16.9.254",
|
||||||
InternalIP: "10.0.0.100",
|
InternalNetmask: "255.255.255.0",
|
||||||
InternalMask: "255.255.255.0",
|
|
||||||
DNSPrimary: "8.8.8.8",
|
DNSPrimary: "8.8.8.8",
|
||||||
DNSSecondary: "8.8.4.4",
|
DNSSecondary: "8.8.4.4",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,43 +45,39 @@ func (m model) View() string {
|
|||||||
|
|
||||||
// agreementView 协议页面
|
// agreementView 协议页面
|
||||||
func (m model) agreementView() string {
|
func (m model) agreementView() string {
|
||||||
title := titleStyle.Render("SunHPC 系统初始化向导")
|
title := titleStyle.Render("SunHPC Software License Agreement")
|
||||||
subtitle := subTitleStyle.Render("请先阅读并同意以下协议")
|
|
||||||
|
|
||||||
agreement := agreementBox.Render(`
|
agreement := agreementBox.Render(`
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
│ SunHPC 软件许可协议 │
|
│ SunHPC License Agreement │
|
||||||
└─────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
1. License Grant
|
||||||
1. 许可授予
|
This software grants you a non-exclusive, non-transferable
|
||||||
本软件授予您非独占、不可转让的使用许可。
|
license to use it.
|
||||||
|
2. Disclaimer of Warranties
|
||||||
2. 使用限制
|
This software is provided "as is" without any warranties,
|
||||||
- 不得用于非法目的
|
whether express or implied.
|
||||||
- 不得反向工程或反编译
|
3. Limitation of Liability
|
||||||
- 不得移除版权标识
|
This software is provided without warranty of any kind,
|
||||||
|
either express or implied.
|
||||||
3. 免责声明
|
In no event shall the author be liable for any damages
|
||||||
本软件按"原样"提供,不提供任何明示或暗示的保证。
|
arising out of the use of this software.
|
||||||
|
4. Termination of Agreement
|
||||||
4. 责任限制
|
If you violate any of the terms of this agreement,
|
||||||
在任何情况下,作者不对因使用本软件造成的任何损失负责。
|
the license will automatically terminate.
|
||||||
|
|
||||||
5. 协议终止
|
|
||||||
如违反本协议条款,许可将自动终止。
|
|
||||||
|
|
||||||
───────────────────────────────────────────────────────────────
|
───────────────────────────────────────────────────────────────
|
||||||
请仔细阅读以上条款,点击"接受"表示您同意并遵守本协议。
|
PLEASE READ THE ABOVE TERMS CAREFULLY AND CLICK "ACCEPT"
|
||||||
|
TO AGREE AND FOLLOW THIS AGREEMENT.
|
||||||
───────────────────────────────────────────────────────────────
|
───────────────────────────────────────────────────────────────
|
||||||
`)
|
`)
|
||||||
|
|
||||||
var acceptBtn, rejectBtn string
|
var acceptBtn, rejectBtn string
|
||||||
if m.agreementIdx == 0 {
|
if m.agreementIdx == 0 {
|
||||||
rejectBtn = selectedButton.Render(">> 拒绝 <<")
|
rejectBtn = selectedButton.Render(">> Reject <<")
|
||||||
acceptBtn = selectedButton.Render(" 同意 ")
|
acceptBtn = " Accept "
|
||||||
} else {
|
} else {
|
||||||
rejectBtn = selectedButton.Render(" 拒绝 ")
|
rejectBtn = " Reject "
|
||||||
acceptBtn = selectedButton.Render(">> 同意 <<")
|
acceptBtn = selectedButton.Render(">> Accept <<")
|
||||||
}
|
}
|
||||||
|
|
||||||
buttonGroup := lipgloss.JoinHorizontal(
|
buttonGroup := lipgloss.JoinHorizontal(
|
||||||
@@ -92,11 +88,9 @@ func (m model) agreementView() string {
|
|||||||
// debugInfo := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).
|
// debugInfo := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).
|
||||||
// Render(fmt.Sprintf("[DEBUG: idx=%d]", m.agreementIdx),)
|
// Render(fmt.Sprintf("[DEBUG: idx=%d]", m.agreementIdx),)
|
||||||
|
|
||||||
hint := hintStyle.Render("使用 <- -> 或 Tab 选择,Enter 确认")
|
hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm")
|
||||||
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Center,
|
return lipgloss.JoinVertical(lipgloss.Center,
|
||||||
title, "",
|
title, "",
|
||||||
subtitle, "",
|
|
||||||
agreement, "",
|
agreement, "",
|
||||||
buttonGroup, "",
|
buttonGroup, "",
|
||||||
// debugInfo, "", // ✅ 显示调试信息
|
// debugInfo, "", // ✅ 显示调试信息
|
||||||
@@ -106,8 +100,7 @@ func (m model) agreementView() string {
|
|||||||
|
|
||||||
// dataView 数据接收页面
|
// dataView 数据接收页面
|
||||||
func (m model) dataView() string {
|
func (m model) dataView() string {
|
||||||
title := titleStyle.Render("集群基础配置")
|
title := titleStyle.Render("Cluster Information")
|
||||||
subtitle := subTitleStyle.Render("请填写系统基本信息")
|
|
||||||
|
|
||||||
var inputs strings.Builder
|
var inputs strings.Builder
|
||||||
for i, ti := range m.textInputs {
|
for i, ti := range m.textInputs {
|
||||||
@@ -117,11 +110,9 @@ func (m model) dataView() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buttons := m.renderNavButtons()
|
buttons := m.renderNavButtons()
|
||||||
|
hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm")
|
||||||
hint := hintStyle.Render("使用 Up/Down 或 Tab 切换、Enter 确认")
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Center,
|
return lipgloss.JoinVertical(lipgloss.Center,
|
||||||
title, "",
|
title, "",
|
||||||
subtitle, "",
|
|
||||||
inputs.String(), "",
|
inputs.String(), "",
|
||||||
buttons, "",
|
buttons, "",
|
||||||
hint,
|
hint,
|
||||||
@@ -130,25 +121,24 @@ func (m model) dataView() string {
|
|||||||
|
|
||||||
// publicNetworkView 公网设置页面
|
// publicNetworkView 公网设置页面
|
||||||
func (m model) publicNetworkView() string {
|
func (m model) publicNetworkView() string {
|
||||||
title := titleStyle.Render("公网配置")
|
title := titleStyle.Render("Public Network Configuration")
|
||||||
subtitle := subTitleStyle.Render("请配置网络接口信息")
|
|
||||||
|
|
||||||
autoDetect := infoStyle.Render("[*] 自动检测网络接口: eth0, eth1, ens33")
|
networkInterfaces := getNetworkInterfaces()
|
||||||
|
autoDetect := infoStyle.Render(
|
||||||
|
"[*] Auto Detect Network Interfaces: " + strings.Join(networkInterfaces, ", "))
|
||||||
|
|
||||||
var inputs strings.Builder
|
var inputs strings.Builder
|
||||||
for i, ti := range m.textInputs {
|
for i, ti := range m.textInputs {
|
||||||
info := fmt.Sprintf("%-10s|", m.inputLabels[i])
|
info := fmt.Sprintf("%-20s|", m.inputLabels[i])
|
||||||
input := inputBox.Render(info + ti.View())
|
input := inputBox.Render(info + ti.View())
|
||||||
inputs.WriteString(input + "\n")
|
inputs.WriteString(input + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons := m.renderNavButtons()
|
buttons := m.renderNavButtons()
|
||||||
|
hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm")
|
||||||
hint := hintStyle.Render("使用 Up/Down 或 Tab 切换、Enter 确认")
|
|
||||||
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Center,
|
return lipgloss.JoinVertical(lipgloss.Center,
|
||||||
title, "",
|
title, "",
|
||||||
subtitle, "",
|
|
||||||
autoDetect, "",
|
autoDetect, "",
|
||||||
inputs.String(), "",
|
inputs.String(), "",
|
||||||
buttons, "",
|
buttons, "",
|
||||||
@@ -158,22 +148,25 @@ func (m model) publicNetworkView() string {
|
|||||||
|
|
||||||
// internalNetworkView 内网配置页面
|
// internalNetworkView 内网配置页面
|
||||||
func (m model) internalNetworkView() string {
|
func (m model) internalNetworkView() string {
|
||||||
title := titleStyle.Render("内网配置")
|
title := titleStyle.Render("Internal Network Configuration")
|
||||||
subtitle := subTitleStyle.Render("请配置内网信息")
|
|
||||||
|
networkInterfaces := getNetworkInterfaces()
|
||||||
|
autoDetect := infoStyle.Render(
|
||||||
|
"[*] Auto Detect Network Interfaces: " + strings.Join(networkInterfaces, ", "))
|
||||||
|
|
||||||
var inputs strings.Builder
|
var inputs strings.Builder
|
||||||
for i, ti := range m.textInputs {
|
for i, ti := range m.textInputs {
|
||||||
info := fmt.Sprintf("%-10s|", m.inputLabels[i])
|
info := fmt.Sprintf("%-20s|", m.inputLabels[i])
|
||||||
input := inputBox.Render(info + ti.View())
|
input := inputBox.Render(info + ti.View())
|
||||||
inputs.WriteString(input + "\n")
|
inputs.WriteString(input + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons := m.renderNavButtons()
|
buttons := m.renderNavButtons()
|
||||||
hint := hintStyle.Render("使用 Up/Down 或 Tab 切换、Enter 确认")
|
hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm")
|
||||||
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Center,
|
return lipgloss.JoinVertical(lipgloss.Center,
|
||||||
title, "",
|
title, "",
|
||||||
subtitle, "",
|
autoDetect, "",
|
||||||
inputs.String(), "",
|
inputs.String(), "",
|
||||||
buttons, "",
|
buttons, "",
|
||||||
hint,
|
hint,
|
||||||
@@ -182,8 +175,7 @@ func (m model) internalNetworkView() string {
|
|||||||
|
|
||||||
// dnsView DNS 配置页面
|
// dnsView DNS 配置页面
|
||||||
func (m model) dnsView() string {
|
func (m model) dnsView() string {
|
||||||
title := titleStyle.Render("DNS 配置")
|
title := titleStyle.Render("DNS Configuration")
|
||||||
subtitle := subTitleStyle.Render("请配置 DNS 服务器")
|
|
||||||
|
|
||||||
var inputs strings.Builder
|
var inputs strings.Builder
|
||||||
for i, ti := range m.textInputs {
|
for i, ti := range m.textInputs {
|
||||||
@@ -193,12 +185,10 @@ func (m model) dnsView() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
buttons := m.renderNavButtons()
|
buttons := m.renderNavButtons()
|
||||||
|
hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm")
|
||||||
hint := hintStyle.Render("使用 Up/Down 或 Tab 切换、Enter 确认")
|
|
||||||
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Center,
|
return lipgloss.JoinVertical(lipgloss.Center,
|
||||||
title, "",
|
title, "",
|
||||||
subtitle, "",
|
|
||||||
inputs.String(), "",
|
inputs.String(), "",
|
||||||
buttons, "",
|
buttons, "",
|
||||||
hint,
|
hint,
|
||||||
@@ -207,71 +197,71 @@ func (m model) dnsView() string {
|
|||||||
|
|
||||||
// summaryView 总结页面
|
// summaryView 总结页面
|
||||||
func (m model) summaryView() string {
|
func (m model) summaryView() string {
|
||||||
title := titleStyle.Render("配置总结")
|
title := titleStyle.Render("Summary")
|
||||||
subtitle := subTitleStyle.Render("请确认以下配置信息")
|
subtitle := subTitleStyle.Render("Please confirm the following configuration information")
|
||||||
|
|
||||||
summary := summaryBox.Render(fmt.Sprintf(`
|
summary := summaryBox.Render(fmt.Sprintf(`
|
||||||
+---------------------------------------------+
|
+----------------------------------------------------+
|
||||||
基本信息
|
Basic Information
|
||||||
+---------------------------------------------+
|
+----------------------------------------------------+
|
||||||
主机名:%-35s
|
Homepage : %-38s
|
||||||
国 家: %-31s
|
Hostname : %-35s
|
||||||
地 区:%-31s
|
Country : %-31s
|
||||||
时 区:%-38s
|
Region : %-31s
|
||||||
主 页:%-38s
|
Timezone : %-38s
|
||||||
+---------------------------------------------+
|
Homepage : %-38s
|
||||||
数据库
|
+----------------------------------------------------+
|
||||||
+---------------------------------------------+
|
Database Configuration
|
||||||
地 址:%-38s
|
+----------------------------------------------------+
|
||||||
名 称:%-38s
|
Database Path : %-38s
|
||||||
软 件:%-33s
|
Software : %-33s
|
||||||
+---------------------------------------------+
|
+----------------------------------------------------+
|
||||||
公网配置
|
Public Network Configuration
|
||||||
+---------------------------------------------+
|
+----------------------------------------------------+
|
||||||
接 口:%-38s
|
Public Interface : %-38s
|
||||||
地 址: %-41s
|
Public IP : %-41s
|
||||||
掩 码:%-38s
|
Public Netmask : %-38s
|
||||||
网 关:%-38s
|
Public Gateway : %-38s
|
||||||
+---------------------------------------------+
|
+----------------------------------------------------+
|
||||||
内网配置
|
Internal Network Configuration
|
||||||
+---------------------------------------------+
|
+----------------------------------------------------+
|
||||||
接 口:%-38s
|
Internal Interface: %-38s
|
||||||
地 址: %-41s
|
Internal IP : %-41s
|
||||||
掩 码:%-38s
|
Internal Netmask : %-38s
|
||||||
+---------------------------------------------+
|
+----------------------------------------------------+
|
||||||
DNS
|
DNS Configuration
|
||||||
+---------------------------------------------+
|
+----------------------------------------------------+
|
||||||
主 DNS: %-37s
|
Primary DNS : %-37s
|
||||||
备 DNS: %-37s
|
Secondary DNS : %-37s
|
||||||
+---------------------------------------------+
|
+----------------------------------------------------+
|
||||||
`,
|
`,
|
||||||
|
m.config.HomePage,
|
||||||
m.config.Hostname,
|
m.config.Hostname,
|
||||||
m.config.Country,
|
m.config.Country,
|
||||||
m.config.Region,
|
m.config.Region,
|
||||||
m.config.Timezone,
|
m.config.Timezone,
|
||||||
m.config.HomePage,
|
m.config.HomePage,
|
||||||
m.config.DBAddress,
|
m.config.DBAddress,
|
||||||
m.config.DBName,
|
|
||||||
m.config.DataAddress,
|
m.config.DataAddress,
|
||||||
m.config.PublicInterface,
|
m.config.PublicInterface,
|
||||||
m.config.IPAddress,
|
m.config.PublicIPAddress,
|
||||||
m.config.Netmask,
|
m.config.PublicNetmask,
|
||||||
m.config.Gateway,
|
m.config.PublicGateway,
|
||||||
m.config.InternalInterface,
|
m.config.InternalInterface,
|
||||||
m.config.InternalIP,
|
m.config.InternalIPAddress,
|
||||||
m.config.InternalMask,
|
m.config.InternalNetmask,
|
||||||
m.config.DNSPrimary,
|
m.config.DNSPrimary,
|
||||||
m.config.DNSSecondary,
|
m.config.DNSSecondary,
|
||||||
))
|
))
|
||||||
|
|
||||||
var buttons string
|
var buttons string
|
||||||
if m.focusIndex == 0 {
|
if m.focusIndex == 0 {
|
||||||
buttons = selectedButton.Render("[>] 执行初始化") + " " + normalButton.Render("[ ] 取消")
|
buttons = selectedButton.Render("[>] Start Initialization") + " " + normalButton.Render("[ ] Cancel")
|
||||||
} else {
|
} else {
|
||||||
buttons = normalButton.Render("[>] 执行初始化") + " " + selectedButton.Render("[ ] 取消")
|
buttons = normalButton.Render("[>] Start Initialization") + " " + selectedButton.Render("[ ] Cancel")
|
||||||
}
|
}
|
||||||
|
|
||||||
hint := hintStyle.Render("使用 <- -> 或 Tab 选择,Enter 确认")
|
hint := hintStyle.Render("Use Up/Down OR Tab Change,Enter Confirm")
|
||||||
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Center,
|
return lipgloss.JoinVertical(lipgloss.Center,
|
||||||
title, "",
|
title, "",
|
||||||
@@ -297,7 +287,7 @@ func progressView(current PageType, total int) string {
|
|||||||
progress += " "
|
progress += " "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
labels := []string{"协议", "数据", "公网", "内网", "DNS", "总结"}
|
labels := []string{"License", "Data", "Network", "Network", "DNS", "Summary"}
|
||||||
label := labelStyle.Render(labels[current])
|
label := labelStyle.Render(labels[current])
|
||||||
return progressStyle.Render(progress) + " " + label
|
return progressStyle.Render(progress) + " " + label
|
||||||
}
|
}
|
||||||
@@ -305,26 +295,26 @@ func progressView(current PageType, total int) string {
|
|||||||
// successView 成功视图
|
// successView 成功视图
|
||||||
func successView() string {
|
func successView() string {
|
||||||
return containerStyle.Render(lipgloss.JoinVertical(lipgloss.Center,
|
return containerStyle.Render(lipgloss.JoinVertical(lipgloss.Center,
|
||||||
successTitle.Render("初始化完成!"), "",
|
successTitle.Render("Initialization Completed!"), "",
|
||||||
successMsg.Render("系统配置已保存,正在初始化..."), "",
|
successMsg.Render("System configuration has been saved, and the system is initializing..."), "",
|
||||||
hintStyle.Render("按任意键退出"),
|
hintStyle.Render("Press any key to exit"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// quitView 退出视图
|
// quitView 退出视图
|
||||||
func quitView() string {
|
func quitView() string {
|
||||||
return containerStyle.Render(lipgloss.JoinVertical(lipgloss.Center,
|
return containerStyle.Render(lipgloss.JoinVertical(lipgloss.Center,
|
||||||
errorTitle.Render("已取消"), "",
|
errorTitle.Render("Canceled"), "",
|
||||||
errorMsg.Render("初始化已取消,未保存任何配置"),
|
errorMsg.Render("Initialization canceled, no configuration saved"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// errorView 错误视图
|
// errorView 错误视图
|
||||||
func errorView(err error) string {
|
func errorView(err error) string {
|
||||||
return containerStyle.Render(lipgloss.JoinVertical(lipgloss.Center,
|
return containerStyle.Render(lipgloss.JoinVertical(lipgloss.Center,
|
||||||
errorTitle.Render("错误"), "",
|
errorTitle.Render("Error"), "",
|
||||||
errorMsg.Render(err.Error()), "",
|
errorMsg.Render(err.Error()), "",
|
||||||
hintStyle.Render("按 Ctrl+C 退出"),
|
hintStyle.Render("Press Ctrl+C to exit"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,9 +322,9 @@ func errorView(err error) string {
|
|||||||
func navButtons(m model, prev, next string) string {
|
func navButtons(m model, prev, next string) string {
|
||||||
var btns string
|
var btns string
|
||||||
if m.currentPage == 0 {
|
if m.currentPage == 0 {
|
||||||
btns = normalButton.Render(prev) + " " + selectedButton.Render(next)
|
btns = normalButton.Render(next) + " " + selectedButton.Render(prev)
|
||||||
} else {
|
} else {
|
||||||
btns = selectedButton.Render(prev) + " " + normalButton.Render(next)
|
btns = selectedButton.Render(next) + " " + normalButton.Render(prev)
|
||||||
}
|
}
|
||||||
return btns
|
return btns
|
||||||
}
|
}
|
||||||
@@ -345,22 +335,22 @@ func (m model) renderNavButtons() string {
|
|||||||
switch m.focusType {
|
switch m.focusType {
|
||||||
case FocusTypePrev:
|
case FocusTypePrev:
|
||||||
// 焦点在"上一步"
|
// 焦点在"上一步"
|
||||||
prevBtn = selectedButton.Render("<< 上一步 >>")
|
nextBtn = normalButton.Render(" Next ")
|
||||||
nextBtn = normalButton.Render("下一步 >>")
|
prevBtn = selectedButton.Render(" << Prev >>")
|
||||||
case FocusTypeNext:
|
case FocusTypeNext:
|
||||||
// 焦点在"下一步"
|
// 焦点在"下一步"
|
||||||
prevBtn = normalButton.Render("<< 上一步")
|
nextBtn = selectedButton.Render(" << Next >>")
|
||||||
nextBtn = selectedButton.Render("<< 下一步 >>")
|
prevBtn = normalButton.Render(" Prev ")
|
||||||
default:
|
default:
|
||||||
// 焦点在输入框
|
// 焦点在输入框
|
||||||
prevBtn = normalButton.Render("<< 上一步")
|
nextBtn = normalButton.Render(" Next ")
|
||||||
nextBtn = normalButton.Render("下一步 >>")
|
prevBtn = normalButton.Render(" Prev ")
|
||||||
}
|
}
|
||||||
|
|
||||||
return lipgloss.JoinHorizontal(
|
return lipgloss.JoinHorizontal(
|
||||||
lipgloss.Center,
|
lipgloss.Center,
|
||||||
prevBtn,
|
|
||||||
" ",
|
|
||||||
nextBtn,
|
nextBtn,
|
||||||
|
" ",
|
||||||
|
prevBtn,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ var normalButton = lipgloss.NewStyle().
|
|||||||
Foreground(lipgloss.Color("#666666")) // 深灰色,更暗
|
Foreground(lipgloss.Color("#666666")) // 深灰色,更暗
|
||||||
|
|
||||||
var selectedButton = lipgloss.NewStyle().
|
var selectedButton = lipgloss.NewStyle().
|
||||||
Padding(0, 2).
|
|
||||||
Foreground(lipgloss.Color("#3d4747ff")). // 亮绿色
|
|
||||||
Bold(true)
|
Bold(true)
|
||||||
|
|
||||||
// 输入框样式
|
// 输入框样式
|
||||||
|
|||||||
Reference in New Issue
Block a user