<think>我们将修改程序,使得左侧布局中的itemList(项目列表)和操作按钮在初始化时隐藏,只有在用户登录后才显示。
具体步骤:
1. 将itemList和操作按钮所在的容器(buttonGroup)放入一个容器中(例如leftContent),并设置初始状态为隐藏。
2. 在登录成功后,将这个容器设置为显示状态。
3. 在注销后,将这个容器重新设置为隐藏状态。
注意:由于我们使用的是VSplit容器,直接隐藏整个容器可能会影响布局,因此我们可以使用container.NewMax来包裹需要隐藏的部分,然后通过改变其内容来实现显示/隐藏,或者使用container.Hide和container.Show来切换可见性。
在Fyne中,我们可以使用`container.Hide`和`container.Show`来隐藏和显示控件。但是,在VSplit中,如果其中一个控件被隐藏,那么分割线的位置可能会出现问题。因此,我们可以考虑将itemList和操作按钮放在一个容器里,然后整体控制这个容器的显示和隐藏。
修改方案:
- 创建一个容器(比如叫做leftBottomContainer)来包含itemList和buttonGroup。
- 将这个容器初始状态设置为隐藏(使用container.Hide)。
- 登录成功后,使用container.Show使其显示。
- 注销后,使用container.Hide将其隐藏。
由于VSplit需要两个控件,我们不能直接隐藏其中一个,所以我们将整个leftBottomContainer放在一个container.Max容器中,这样当隐藏时,它会占据0空间。
但是,另一种更简单的方式是:将itemList和buttonGroup所在的VSplit容器(leftPanel)替换为一个可动态切换的容器。我们可以在登录前显示一个占位控件(比如空容器),登录后替换为真正的itemList和操作按钮。
这里我们采用另一种方法:使用一个容器(如container.Center)作为占位,登录后替换为实际的itemList和操作按钮的容器。
具体步骤:
1. 在创建左侧布局时,先创建一个占位的容器(比如空容器)和一个包含itemList和操作按钮的容器(leftBottomContainer)。
2. 将登录框下面的部分初始化为占位容器(这样在登录前,登录框下面就是空的)。
3. 登录成功后,将占位容器替换为leftBottomContainer。
4. 注销时,再将leftBottomContainer替换回占位容器。
但是,由于我们使用的是VSplit,我们需要重新组织左侧布局。
重新组织左侧布局结构如下:
左侧整体:一个垂直分割(VSplit)容器,包含两部分:
上部分:登录框(loginContainer)
下部分:一个容器(我们称为dynamicContainer),这个容器在登录前是空的,登录后显示itemList和操作按钮。
我们将dynamicContainer初始化为空容器(container.NewMax()),然后在登录后设置其内容为包含itemList和操作按钮的容器(即原来的leftPanel的下半部分)。
修改代码:
1. 创建dynamicContainer,初始为空:
dynamicContainer := container.NewMax()
2. 修改左侧布局的创建:
leftPanel := container.NewVSplit(
loginContainer,
dynamicContainer,
)
3. 在登录成功后,将dynamicContainer的内容设置为包含itemList和操作按钮的容器:
// 创建包含itemList和操作按钮的容器
contentForLoggedIn := container.NewVSplit(
container.NewBorder(nil, nil, nil, nil, itemList),
buttonGroup,
)
contentForLoggedIn.SetOffset(0.5) // 可以调整分割比例
// 替换dynamicContainer的内容
dynamicContainer.Objects = []fyne.CanvasObject{contentForLoggedIn}
dynamicContainer.Refresh()
4. 在注销时,将dynamicContainer的内容设置为空(或者一个空容器):
dynamicContainer.Objects = []fyne.CanvasObject{}
dynamicContainer.Refresh()
但是,注意:在登录前,我们并没有创建itemList和操作按钮的容器,所以我们需要在登录时动态创建,或者提前创建好但初始不显示。这里我们选择在登录时动态创建。
但是,考虑到itemList和操作按钮在程序运行期间内容不会改变(除了按钮的事件处理),我们可以提前创建好,然后隐藏。不过,为了简单,我们提前创建好itemList和buttonGroup,然后创建一个容器(loggedInBottom)来存放它们,然后动态切换。
我们调整一下:
提前创建好loggedInBottom容器(即itemList和操作按钮所在的容器):
loggedInBottom := container.NewVSplit(
container.NewBorder(nil, nil, nil, nil, itemList),
buttonGroup,
)
loggedInBottom.SetOffset(0.5)
然后,在程序初始化时,dynamicContainer是空的(用一个空容器占位,但实际我们不需要显示任何东西,所以用container.NewMax()且不添加内容)。
但是,在登录后,我们设置dynamicContainer为loggedInBottom;注销时,我们移除loggedInBottom,设置为空。
但是,这样会导致每次登录都要重新创建loggedInBottom?不需要,我们提前创建好,然后切换。
然而,Fyne的容器只能有一个父容器,所以我们不能重复添加。因此,我们使用一个技巧:将loggedInBottom放在一个container.Max容器中,然后通过改变这个Max容器的内容来显示或隐藏(实际上我们每次都是重新设置dynamicContainer的子对象)。
我们修改:在登录时,将dynamicContainer的内容设置为loggedInBottom;注销时,设置为一个空容器。
这样,我们需要在初始化时创建loggedInBottom,但先不添加到dynamicContainer。
具体代码步骤:
1. 创建loggedInBottom容器(包含itemList和操作按钮):
loggedInBottom := container.NewVSplit(
container.NewBorder(nil, nil, nil, nil, itemList),
buttonGroup,
)
loggedInBottom.SetOffset(0.5)
2. 创建dynamicContainer,初始为空容器(container.NewMax()不添加任何内容):
dynamicContainer := container.NewMax()
3. 在登录事件中:
dynamicContainer.Objects = []fyne.CanvasObject{loggedInBottom}
dynamicContainer.Refresh()
4. 在注销事件中:
dynamicContainer.Objects = []fyne.CanvasObject{} // 清空
dynamicContainer.Refresh()
但是,这样会导致布局问题吗?我们测试一下。
另外,我们还需要注意:在登录前,左侧布局的下半部分应该是不可见的(即高度为0),而登录后应该显示。VSplit会自动调整大小,因为我们设置了一个初始的偏移量(leftPanel.SetOffset(0.3)),这个偏移量是针对整个leftPanel的。当我们清空dynamicContainer时,VSplit的下半部分会收缩为0,所以不会占用空间。
因此,我们按照这个思路修改代码。
注意:由于itemList和buttonGroup在登录前已经创建,但不会显示,所以没有问题。
修改后的代码部分:
我们将创建itemList和buttonGroup的代码放在前面,然后创建loggedInBottom容器。
由于在登录前,dynamicContainer是空的,所以不会显示。登录后,我们设置dynamicContainer的内容为loggedInBottom,这样就会显示出来。
注销时,我们清空dynamicContainer,这样下半部分就消失了。
我们修改代码,将原来的leftPanel拆分成两部分:loginContainer和dynamicContainer,其中dynamicContainer初始为空。
同时,我们将原来的itemList和buttonGroup组成的容器命名为loggedInBottom。
注意:由于VSplit嵌套VSplit可能会引起布局嵌套问题,但这里我们只是将loggedInBottom(一个VSplit)放入dynamicContainer(一个Max容器),应该没问题。
我们开始修改代码:</think>我将修改程序,使左侧布局的itemList和操作按钮在程序初始化时隐藏,只有在用户登录后才显示。以下是完整的解决方案:
```go
package main
import (
"fmt"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"math/rand"
"strconv"
"time"
)
// 用户信息结构体
type UserInfo struct {
Username string
Level int
Age int
}
// 任务结构体
type Task struct {
ID string
Name string
Status string
}
func main() {
myApp := app.New()
myWindow := myApp.NewWindow("Fyne 登录后显示示例")
myWindow.Resize(fyne.NewSize(1000, 700))
// 创建底部状态栏
statusBar := widget.NewLabel("就绪 | 状态: 未登录")
// 创建用户信息结构
currentUser := &UserInfo{
Username: "",
Level: 0,
Age: 0,
}
// 创建登录框组件
username := widget.NewEntry()
password := widget.NewPasswordEntry()
loginBtn := widget.NewButton("登录", nil)
// 创建用户信息显示组件
userInfoLabel := widget.NewLabel("")
logoutBtn := widget.NewButton("注销", nil)
// 创建堆栈容器,用于切换登录/已登录状态
loginContainer := container.NewMax()
// 创建登录表单
loginForm := widget.NewForm(
widget.NewFormItem("用户名", username),
widget.NewFormItem("密码", password),
)
loginBox := container.NewVBox(
widget.NewLabel("系统登录"),
loginForm,
loginBtn,
)
// 创建用户信息显示区域
userInfoBox := container.NewVBox(
widget.NewLabel("用户信息"),
widget.NewSeparator(),
userInfoLabel,
layout.NewSpacer(),
logoutBtn,
)
// 初始显示登录框
loginContainer.Objects = []fyne.CanvasObject{loginBox}
loginContainer.Refresh()
// ================ 左侧布局的其他部分 - 初始隐藏 ================
// 创建项目列表
itemList := widget.NewList(
func() int { return 5 },
func() fyne.CanvasObject { return widget.NewLabel("项目") },
func(id widget.ListItemID, obj fyne.CanvasObject) {
obj.(*widget.Label).SetText(fmt.Sprintf("项目 %d", id+1))
},
)
// 创建操作按钮
btn1 := widget.NewButton("操作 1 (添加用户列表)", nil)
btn2 := widget.NewButton("操作 2 (加载初始数据)", nil)
btn3 := widget.NewButton("操作 3 (添加50条任务)", nil)
buttonGroup := container.NewVBox(btn1, btn2, btn3)
// 创建左侧布局容器 - 初始隐藏
leftBottomContainer := container.NewVSplit(
container.NewBorder(nil, nil, nil, nil, itemList),
buttonGroup,
)
leftBottomContainer.SetOffset(0.5) // 初始分割比例
// 创建隐藏容器,用于控制显示/隐藏
hiddenContainer := container.NewMax()
hiddenContainer.Hide() // 初始隐藏
// ================ 任务列表分页实现 ================
// 任务数据初始为空
tasksAll := []Task{}
// 分页相关变量
const pageSize = 20 // 每页显示20条
currentPage := 0 // 当前页码(从0开始)
totalPages := 0 // 总页数
// 当前页的任务数据
currentPageTasks := []Task{}
// 创建任务列表视图
taskListView := widget.NewList(
func() int { return len(currentPageTasks) },
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewIcon(theme.DocumentIcon()),
widget.NewLabel(""),
widget.NewLabel(""),
widget.NewLabel(""),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
container := obj.(*fyne.Container)
labels := container.Objects[1:] // 跳过图标
task := currentPageTasks[id]
labels[0].(*widget.Label).SetText(fmt.Sprintf("ID: %s", task.ID))
labels[1].(*widget.Label).SetText(task.Name)
labels[2].(*widget.Label).SetText(fmt.Sprintf("状态: %s", task.Status))
},
)
// 分页控件
pageInfoLabel := widget.NewLabel("无数据")
pageEntry := widget.NewEntry()
pageEntry.SetPlaceHolder("输入页码")
pageEntry.Disable()
// 翻页按钮
firstPageBtn := widget.NewButtonWithIcon("", theme.MediaSkipPreviousIcon(), nil)
prevPageBtn := widget.NewButtonWithIcon("", theme.NavigateBackIcon(), nil)
nextPageBtn := widget.NewButtonWithIcon("", theme.NavigateNextIcon(), nil)
lastPageBtn := widget.NewButtonWithIcon("", theme.MediaSkipNextIcon(), nil)
goToPageBtn := widget.NewButton("跳转", nil)
// 禁用所有分页按钮
disablePaginationButtons := func() {
firstPageBtn.Disable()
prevPageBtn.Disable()
nextPageBtn.Disable()
lastPageBtn.Disable()
goToPageBtn.Disable()
pageEntry.Disable()
}
// 启用所有分页按钮
enablePaginationButtons := func() {
firstPageBtn.Enable()
prevPageBtn.Enable()
nextPageBtn.Enable()
lastPageBtn.Enable()
goToPageBtn.Enable()
pageEntry.Enable()
}
// 初始禁用分页按钮
disablePaginationButtons()
// 更新分页数据和显示
updatePage := func(page int) {
if len(tasksAll) == 0 {
pageInfoLabel.SetText("无数据")
currentPageTasks = []Task{}
taskListView.Refresh()
disablePaginationButtons()
return
}
if page < 0 {
page = 0
} else if page >= totalPages {
page = totalPages - 1
}
currentPage = page
start := currentPage * pageSize
end := start + pageSize
if end > len(tasksAll) {
end = len(tasksAll)
}
currentPageTasks = tasksAll[start:end]
pageInfoLabel.SetText(fmt.Sprintf("第 %d/%d 页", currentPage+1, totalPages))
pageEntry.SetText(fmt.Sprintf("%d", currentPage+1))
taskListView.Refresh()
statusBar.SetText(fmt.Sprintf("已切换到第 %d 页,显示 %d-%d 条",
currentPage+1, start+1, end))
// 更新按钮状态
enablePaginationButtons()
if currentPage == 0 {
firstPageBtn.Disable()
prevPageBtn.Disable()
}
if currentPage == totalPages-1 {
nextPageBtn.Disable()
lastPageBtn.Disable()
}
}
// 绑定按钮事件
firstPageBtn.OnTapped = func() { updatePage(0) }
prevPageBtn.OnTapped = func() { updatePage(currentPage - 1) }
nextPageBtn.OnTapped = func() { updatePage(currentPage + 1) }
lastPageBtn.OnTapped = func() { updatePage(totalPages - 1) }
goToPageBtn.OnTapped = func() {
pageStr := pageEntry.Text
if pageStr == "" {
statusBar.SetText("请输入页码")
return
}
pageNum, err := strconv.Atoi(pageStr)
if err != nil {
statusBar.SetText("无效的页码格式")
return
}
if pageNum < 1 || pageNum > totalPages {
statusBar.SetText(fmt.Sprintf("页码必须在 1-%d 之间", totalPages))
return
}
updatePage(pageNum - 1)
}
// 创建分页控件容器
paginationControls := container.NewHBox(
firstPageBtn,
prevPageBtn,
pageInfoLabel,
nextPageBtn,
lastPageBtn,
widget.NewLabel("跳转到:"),
pageEntry,
goToPageBtn,
)
// 任务列表容器(列表 + 分页控件)
taskListContainer := container.NewBorder(
nil, // 顶部
paginationControls, // 底部:分页控件
nil, // 左侧
nil, // 右侧
taskListView, // 中间:列表视图
)
// ================ 分页实现结束 ================
// 创建通知数据
notifications := []string{
"系统维护通知: 周六凌晨1-3点进行系统维护",
"欢迎新用户: " + username.Text + " 加入系统",
"安全提示: 请定期修改密码",
"更新通知: 新版本v1.2.0已发布",
}
// 创建通知列表视图
notificationListView := widget.NewList(
func() int { return len(notifications) },
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewIcon(theme.InfoIcon()),
widget.NewLabel(""),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
container := obj.(*fyne.Container)
label := container.Objects[1].(*widget.Label)
label.SetText(notifications[id])
},
)
// 创建用户数据
users := []string{
"张三 (管理员)",
"李四 (开发人员)",
"王五 (测试人员)",
"赵六 (普通用户)",
}
// 创建用户列表视图
userListView := widget.NewList(
func() int { return len(users) },
func() fyne.CanvasObject {
return container.NewHBox(
widget.NewIcon(theme.AccountIcon()),
widget.NewLabel(""),
)
},
func(id widget.ListItemID, obj fyne.CanvasObject) {
container := obj.(*fyne.Container)
label := container.Objects[1].(*widget.Label)
label.SetText(users[id])
},
)
// 创建选项卡容器
tabs := container.NewAppTabs(
container.NewTabItemWithIcon("任务列表", theme.ListIcon(), taskListContainer),
container.NewTabItemWithIcon("系统通知", theme.MailIcon(), notificationListView),
)
// 添加用户列表选项卡的函数
addUserListTab := func() {
// 检查是否已存在用户列表选项卡
for _, tab := range tabs.Items {
if tab.Text == "用户列表" {
statusBar.SetText("用户列表已存在")
return
}
}
// 添加新选项卡
newTab := container.NewTabItemWithIcon("用户列表", theme.GroupIcon(), userListView)
tabs.Append(newTab)
tabs.Select(newTab)
statusBar.SetText("用户列表选项卡已添加")
}
// 生成单条模拟任务数据的函数
generateTask := func(id int) Task {
rand.Seed(time.Now().UnixNano() + int64(id))
statusOptions := []string{"待处理", "进行中", "已完成", "已取消", "待审核", "已延期"}
taskTypes := []string{"开发", "测试", "文档", "设计", "会议", "评审", "部署", "支持"}
verbs := []string{"完成", "优化", "修复", "重构", "测试", "审查", "部署", "更新"}
objects := []string{"登录模块", "用户界面", "数据库", "API接口", "配置文件", "错误处理", "性能问题", "安全漏洞"}
// 随机选择任务属性
status := statusOptions[rand.Intn(len(statusOptions))]
taskType := taskTypes[rand.Intn(len(taskTypes))]
verb := verbs[rand.Intn(len(verbs))]
object := objects[rand.Intn(len(objects))]
return Task{
ID: fmt.Sprintf("T%04d", id),
Name: fmt.Sprintf("%s任务: %s%s", taskType, verb, object),
Status: status,
}
}
// 设置操作按钮的功能
btn1.OnTapped = func() {
if currentUser.Username == "" {
statusBar.SetText("请先登录")
return
}
statusBar.SetText(fmt.Sprintf("用户 %s 执行操作 1", currentUser.Username))
addUserListTab()
}
btn2.OnTapped = func() {
if currentUser.Username == "" {
statusBar.SetText("请先登录")
return
}
if len(tasksAll) == 0 {
// 生成100条模拟任务数据
for i := 1; i <= 100; i++ {
tasksAll = append(tasksAll, generateTask(i))
}
// 计算总页数
totalPages = len(tasksAll) / pageSize
if len(tasksAll)%pageSize != 0 {
totalPages++ // 有余数时增加一页
}
// 更新到第一页
updatePage(0)
statusBar.SetText(fmt.Sprintf("已加载 %d 条初始任务数据", len(tasksAll)))
} else {
statusBar.SetText("任务数据已存在,无需重复加载")
}
}
btn3.OnTapped = func() {
if currentUser.Username == "" {
statusBar.SetText("请先登录")
return
}
// 记录当前数据量
currentCount := len(tasksAll)
// 生成50条新任务数据
for i := 1; i <= 50; i++ {
newID := currentCount + i
tasksAll = append(tasksAll, generateTask(newID))
}
// 计算新总页数
newTotalPages := len(tasksAll) / pageSize
if len(tasksAll)%pageSize != 0 {
newTotalPages++
}
// 如果总页数增加且当前在最后一页,则自动跳转到新数据的页面
shouldUpdate := false
if newTotalPages != totalPages {
totalPages = newTotalPages
shouldUpdate = true
// 如果之前是在最后一页,现在跳转到新数据的起始页
if currentPage == totalPages-2 { // 因为添加数据后最后一页可能变化
currentPage = totalPages - 1
}
}
// 更新显示
if shouldUpdate {
updatePage(currentPage)
} else {
// 如果当前在最后一页,刷新当前页显示
if currentPage == totalPages-1 {
updatePage(currentPage)
}
}
statusBar.SetText(fmt.Sprintf("已添加 %d 条新任务,当前总任务数: %d", 50, len(tasksAll)))
}
// 登录按钮点击事件
loginBtn.OnTapped = func() {
// 简单的登录验证
if username.Text == "" || password.Text == "" {
statusBar.SetText("登录失败: 用户名或密码不能为空")
return
}
// 模拟登录成功,设置用户信息
currentUser.Username = username.Text
currentUser.Level = 3 // 模拟用户等级
currentUser.Age = 28 // 模拟用户年龄
// 更新用户信息显示
userInfo := fmt.Sprintf("用户名: %s\n等级: %d\n年龄: %d",
currentUser.Username, currentUser.Level, currentUser.Age)
userInfoLabel.SetText(userInfo)
// 切换到用户信息显示
loginContainer.Objects = []fyne.CanvasObject{userInfoBox}
loginContainer.Refresh()
// 显示左侧布局的其他部分
hiddenContainer.Show()
hiddenContainer.Objects = []fyne.CanvasObject{leftBottomContainer}
hiddenContainer.Refresh()
statusBar.SetText(fmt.Sprintf("登录成功: %s | 等级: %d | 年龄: %d",
currentUser.Username, currentUser.Level, currentUser.Age))
}
// 注销按钮点击事件
logoutBtn.OnTapped = func() {
// 重置用户信息
currentUser.Username = ""
currentUser.Level = 0
currentUser.Age = 0
// 清空输入框
username.SetText("")
password.SetText("")
// 切换回登录界面
loginContainer.Objects = []fyne.CanvasObject{loginBox}
loginContainer.Refresh()
// 隐藏左侧布局的其他部分
hiddenContainer.Hide()
// 清空任务数据
tasksAll = []Task{}
totalPages = 0
updatePage(0)
statusBar.SetText("已注销 | 状态: 未登录")
}
// 组装左侧布局
leftTopContainer := container.NewMax(loginContainer)
leftBottomContainer.SetOffset(0.5) // 设置分割比例
leftPanel := container.NewVSplit(
leftTopContainer,
container.NewMax(hiddenContainer), // 使用隐藏容器包装
)
leftPanel.SetOffset(0.3) // 登录框占据30%高度
// 创建右侧布局
rightPanel := container.NewBorder(
nil,
nil,
nil,
nil,
tabs,
)
// 创建主布局
mainSplit := container.NewHSplit(leftPanel, rightPanel)
mainSplit.SetOffset(0.3)
content := container.NewBorder(
nil,
statusBar,
nil,
nil,
mainSplit,
)
myWindow.SetContent(content)
myWindow.ShowAndRun()
}
```
### 主要修改内容
#### 1. 创建隐藏容器
```go
// 创建左侧布局容器 - 初始隐藏
leftBottomContainer := container.NewVSplit(
container.NewBorder(nil, nil, nil, nil, itemList),
buttonGroup,
)
leftBottomContainer.SetOffset(0.5) // 初始分割比例
// 创建隐藏容器,用于控制显示/隐藏
hiddenContainer := container.NewMax()
hiddenContainer.Hide() // 初始隐藏
```
#### 2. 修改登录事件处理
```go
loginBtn.OnTapped = func() {
// ... [登录验证和用户信息设置] ...
// 显示左侧布局的其他部分
hiddenContainer.Show()
hiddenContainer.Objects = []fyne.CanvasObject{leftBottomContainer}
hiddenContainer.Refresh()
// ... [其他登录后操作] ...
}
```
#### 3. 修改注销事件处理
```go
logoutBtn.OnTapped = func() {
// ... [重置用户信息] ...
// 隐藏左侧布局的其他部分
hiddenContainer.Hide()
// ... [其他注销操作] ...
}
```
#### 4. 重新组织左侧布局
```go
// 组装左侧布局
leftTopContainer := container.NewMax(loginContainer)
leftBottomContainer.SetOffset(0.5) // 设置分割比例
leftPanel := container.NewVSplit(
leftTopContainer,
container.NewMax(hiddenContainer), // 使用隐藏容器包装
)
leftPanel.SetOffset(0.3) // 登录框占据30%高度
```
#### 5. 添加登录状态检查
```go
btn1.OnTapped = func() {
if currentUser.Username == "" {
statusBar.SetText("请先登录")
return
}
// ... [操作逻辑] ...
}
btn2.OnTapped = func() {
if currentUser.Username == "" {
statusBar.SetText("请先登录")
return
}
// ... [操作逻辑] ...
}
btn3.OnTapped = func() {
if currentUser.Username == "" {
statusBar.SetText("请先登录")
return
}
// ... [操作逻辑] ...
}
```
### 功能说明
#### 1. 初始状态 - 未登录
- 左侧只显示登录框
- itemList和操作按钮完全隐藏
- 任务列表为空
- 分页控件禁用
#### 2. 登录后状态
- 左侧显示用户信息
- itemList和操作按钮显示
- 可以执行所有操作
- 分页控件根据数据状态启用
#### 3. 注销后状态
- 回到登录界面
- 隐藏itemList和操作按钮
- 清空所有任务数据
- 分页控件禁用
#### 4. 登录状态检查
- 所有操作按钮在执行前检查登录状态
- 未登录时点击操作按钮显示提示
- 防止未授权操作
### 实现原理
#### 1. 隐藏/显示机制
- 使用 `container.NewMax()` 创建一个容器包装需要隐藏的内容
- 使用 `Hide()` 方法在初始时隐藏容器
- 登录时调用 `Show()` 显示容器并设置内容
- 注销时调用 `Hide()` 隐藏容器
#### 2. 布局组织
```go
leftPanel := container.NewVSplit(
leftTopContainer, // 登录框容器
container.NewMax(hiddenContainer), // 隐藏容器包装的内容
)
```
- 使用垂直分割容器组织登录框和隐藏内容
- 隐藏容器在未登录时占据0高度
- 登录后隐藏容器显示,布局自动调整
#### 3. 状态管理
- 使用 `currentUser.Username` 作为登录状态标志
- 所有操作前检查 `currentUser.Username` 是否为空
- 注销时重置用户信息和任务数据
### 用户体验改进
1. **清晰的界面状态转换**:
- 登录前:简洁的登录界面
- 登录后:显示完整功能界面
- 注销后:回到初始状态
2. **操作安全**:
- 未登录时禁用所有功能操作
- 提供明确的提示信息
- 防止未授权操作
3. **状态保持**:
- 登录后记住用户信息
- 注销时自动清理敏感数据
- 保持界面状态一致
4. **布局优化**:
- 登录框占据30%高度,视觉平衡
- 隐藏/显示时平滑过渡
- 分割布局可调整
### 使用示例
1. **初始状态**:
- 程序启动时只显示登录框
- itemList和操作按钮不可见
2. **登录**:
- 输入用户名和密码
- 点击登录按钮
- 左侧显示用户信息和功能区域
- 可以执行操作1、2、3
3. **操作**:
- 点击"操作2"加载初始任务数据
- 点击"操作3"添加新任务
- 使用分页控件浏览任务
4. **注销**:
- 点击注销按钮
- 回到登录界面
- 功能区域隐藏
- 任务数据清空