package ui import ( "encoding/json" "fmt" "io/fs" "io/ioutil" "path/filepath" "sort" "strings" "sync/atomic" "time" "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" "fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "github.com/dengsgo/math-engine/engine" "github.com/sirupsen/logrus" "github.com/spf13/cast" "legu.airobot/lib" "legu.airobot/storage" ) var globalWindow *MainWindow var _ IWindow = (*MainWindow)(nil) type MainWindow struct { UIImpl ai *lib.MyAI w fyne.Window statusbar *statusBar //状态栏 mainMenu *mainMenu //菜单 progress *widget.ProgressBar //进度条 progressCounter int32 //进度条计数 endProgress chan interface{} progressChan chan struct{} tipLabel *widget.Label //提示Label startBtn *widget.Button //开始按钮 robotCount int32 } func NewMainWdindow(ui *UIImpl) IWindow { mw := &MainWindow{ UIImpl: *ui, progressChan: make(chan struct{}), endProgress: make(chan interface{}, 1), } globalWindow = mw return mw } func (mw *MainWindow) CreateWindow(_ string, width, height float32, visible bool) { title := fmt.Sprintf(APP_WIN_TITLE, APP_NAME, mw.app.Metadata().Version, mw.app.Metadata().Build) w := mw.app.NewWindow(title) mw.w = w // 状态栏 mw.statusbar = newStatusBar() // 菜单 mw.mainMenu = newMainMenu() mw.w.SetMainMenu(mw.mainMenu.MainMenu) //content content := container.NewBorder(nil, mw.statusbar.widget, nil, nil) mw.w.SetContent(content) // 默认 mw.startContainer() w.Resize(fyne.NewSize(width, height)) w.SetMaster() w.CenterOnScreen() w.SetCloseIntercept(mw.quiteHandle) w.Show() } func (mw *MainWindow) changeContent(content fyne.CanvasObject) { mw.w.SetContent(content) } // 开始 func (mw *MainWindow) startContainer() { mw.startBtn = widget.NewButton("启动", nil) mw.startBtn.Disable() config := mw.config // 检查全局配置 if config.Global == nil { dialog.ShowCustomConfirm("提示", "设置", "取消", widget.NewLabel("还没有任何配置"), func(b bool) { if !b { return } mw.configContainer() }, mw.w) return } // 检查场景配置 if config.Scenes == nil { dialog.ShowCustomConfirm("提示", "选择", "取消", widget.NewLabel("还没有选择任何场景"), func(b bool) { if !b { return } mw.newSceneContainer() }, mw.w) return } mw.progress = widget.NewProgressBar() mw.tipLabel = widget.NewLabel("") mw.UIImpl.obs.AddListener(lib.EVENT_PROGRESS, lib.Listener{ OnNotify: mw.showProgress, }) mw.UIImpl.obs.AddListener(lib.EVENT_ROBOT, lib.Listener{ OnNotify: mw.listenRobot, }) mw.startBtn.Enable() mw.startBtn.OnTapped = func() { atomic.StoreInt32(&mw.robotCount, 0) atomic.StoreInt32(&mw.progressCounter, 0) mw.progress.SetValue(0) mw.tipLabel.SetText("进行中...") mw.startBtn.Disable() param := lib.AIParam{ Scenes: mw.UIImpl.scenes, Config: mw.UIImpl.config, } ai, err := lib.NewAI(param) if err != nil { dialog.ShowError(err, mw.w) return } ai.Obs = mw.UIImpl.obs mw.ai = ai ai.Start() mw.showReport() } content := container.NewBorder(mw.progress, nil, nil, nil, container.NewCenter(container.NewVBox(mw.tipLabel, mw.startBtn))) mw.changeContent(content) } func (mw *MainWindow) showReport() { start := time.Now() <-mw.progressChan logrus.Debug("开始生成测试报告") elipse := time.Since(start) mw.ai.MergeResult() mw.ai.GenReport(elipse) mw.startBtn.Enable() atomic.StoreInt32(&mw.progressCounter, 0) mw.tipLabel.SetText("已生成报表") } func (mw *MainWindow) showProgress(data interface{}, args ...interface{}) { count := data.(int32) rc := atomic.LoadInt32(&mw.progressCounter) atomic.StoreInt32(&mw.progressCounter, count+rc) //计算进度 max := int(mw.config.Global.UserCountTotal) * len(mw.config.Scenes) expr := fmt.Sprintf("%v/%v", atomic.LoadInt32(&mw.progressCounter), max) logrus.Debug(expr) r, err := engine.ParseAndExec(expr) if err != nil { logrus.Error(err) return } mw.progress.SetValue(r) if r >= 1 { mw.tipLabel.SetText("正在处理结果...") mw.progressChan <- struct{}{} } } func (mw *MainWindow) listenRobot(data interface{}, args ...interface{}) { count := data.(int32) rc := atomic.LoadInt32(&mw.robotCount) atomic.StoreInt32(&mw.robotCount, count+rc) mw.tipLabel.SetText(fmt.Sprintf("投放用户数:%d/%d", atomic.LoadInt32(&mw.robotCount), mw.config.Global.UserCountTotal)) } // 全局配置 func (mw *MainWindow) configContainer() { config := mw.config.Global wsAddrEntry := widget.NewEntry() sidEntry := widget.NewEntry() userCountEntry := widget.NewEntry() userCountEntry.PlaceHolder = "每次压入的用户数" userTotalEntry := widget.NewEntry() userTotalEntry.PlaceHolder = "总共需要压入的最大用户数" timeoutEntry := widget.NewEntry() timeoutEntry.PlaceHolder = "请求超时时间" intervalEntry := widget.NewEntry() intervalEntry.PlaceHolder = "每次压入用户间隔时间" form := widget.NewForm( widget.NewFormItem("服务地址", wsAddrEntry), widget.NewFormItem("区服编号", sidEntry), widget.NewFormItem("用户数上限", userTotalEntry), widget.NewFormItem("用户数", userCountEntry), widget.NewFormItem("间隔时间(s)", intervalEntry), widget.NewFormItem("超时时间(ms)", timeoutEntry), ) loadForm := func() { wsAddrEntry.Text = config.WsAddr sidEntry.Text = config.SId userTotalEntry.Text = cast.ToString(config.UserCountTotal) userCountEntry.Text = cast.ToString(config.UserCount) timeoutEntry.Text = cast.ToString(config.TimeoutMs) intervalEntry.Text = cast.ToString(config.IntervalS) } if config != nil { loadForm() } saveBtn := widget.NewButtonWithIcon("保存", theme.DocumentSaveIcon(), nil) saveBtn.OnTapped = func() { global := &storage.Global{ WsAddr: wsAddrEntry.Text, SId: sidEntry.Text, UserCountTotal: cast.ToUint32(userTotalEntry.Text), UserCount: cast.ToUint32(userCountEntry.Text), TimeoutMs: cast.ToInt32(timeoutEntry.Text), IntervalS: cast.ToInt32(intervalEntry.Text), } mw.config.Global = global if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } } btns := container.NewHBox(layout.NewSpacer(), saveBtn) content := container.NewVBox(form, btns) mw.changeContent(content) } //旧场景 func (mw *MainWindow) sceneContainer() { var selSceneName string // 用例列表 callerItemList := *lib.NewItemList() callerItemList.ListWidget = callerItemList.CreateList() callerLoad := func(sceneId string) { callerItemList.Reset() for _, v := range mw.config.Scenes { if v.ID == sceneId { sort.SliceStable(v.Callers, func(i, j int) bool { return v.Callers[i].Num < v.Callers[j].Num }) for _, v2 := range v.Callers { item := lib.Item{ Id: cast.ToString(v2.ID), Text: fmt.Sprintf("%d. %v %v", v2.Num, v2.Name, v2.Key), } callerItemList.AddItem(item) } } } } //场景列表 sceneItemList := *lib.NewItemList() sceneItemList.OnSelFunc = func(id widget.ListItemID) { selId := sceneItemList.CachedList.Items[id].Id selSceneName = sceneItemList.CachedList.Items[id].Text sceneItemList.SelItemId = selId callerLoad(selId) } sceneItemList.ListWidget = sceneItemList.CreateList() sceneLoad := func() { sceneItemList.Reset() logrus.WithField("场景", mw.config.Scenes).Debug("配置") for _, v := range mw.config.Scenes { txt := fmt.Sprintf("%v %s", v.Name, v.Desc) if v.Status == lib.STATUS_ENABLE { txt = fmt.Sprintf("* %s %s", v.Name, v.Desc) } item := lib.Item{ Id: cast.ToString(v.ID), Text: txt, } sceneItemList.AddItem(item) } } // 场景表单 sceneNameEntry := widget.NewEntry() sceneDescEntry := widget.NewEntry() sceneItems := []*widget.FormItem{ widget.NewFormItem("名称", sceneNameEntry), widget.NewFormItem("描述", sceneDescEntry), } //重置场景表单 sceneFormReset := func() { sceneNameEntry.Text = "" sceneDescEntry.Text = "" } // 用例表单 callerNameEntry := widget.NewEntry() callerKeyEntry := widget.NewEntry() callerNumEntry := widget.NewEntry() callerNumEntry.PlaceHolder = "0" callerItems := []*widget.FormItem{ widget.NewFormItem("名称", callerNameEntry), widget.NewFormItem("协议", callerKeyEntry), widget.NewFormItem("序号", callerNumEntry), } //重置用例表单 callerFormReset := func() { callerNameEntry.Text = "" callerKeyEntry.Text = "" } // 场景按钮 newSceneBtn := widget.NewButtonWithIcon("新建场景", theme.FileIcon(), nil) editSceneBtn := widget.NewButtonWithIcon("编辑场景", theme.DocumentCreateIcon(), nil) deleSceneBtn := widget.NewButtonWithIcon("删除场景", theme.DeleteIcon(), nil) enableSceneBtn := widget.NewButton("启用场景", nil) // 添加用例按钮 newCallerBtn := widget.NewButtonWithIcon("添加用例", theme.FileIcon(), nil) deleCallerBtn := widget.NewButtonWithIcon("删除用例", theme.DocumentCreateIcon(), nil) // 刷新场景 refreshBtn := widget.NewButtonWithIcon("", theme.ViewRefreshIcon(), nil) // 工具条 toolbar := container.NewHBox(newSceneBtn, editSceneBtn, deleSceneBtn, enableSceneBtn, widget.NewSeparator(), newCallerBtn, deleCallerBtn, layout.NewSpacer(), refreshBtn) //布局 var dynamic fyne.CanvasObject contentRender := func() { sceneLoad() if len(sceneItemList.CachedList.Items) == 0 { dynamic = container.NewCenter(widget.NewLabel("还没有创建任何场景")) } else { split := container.NewHSplit(sceneItemList.ListWidget, callerItemList.ListWidget) split.Offset = 0.3 dynamic = split } content := container.NewBorder(toolbar, nil, nil, nil, dynamic) mw.changeContent(content) } contentRender() // 刷新场景 refreshBtn.OnTapped = func() { sceneItemList.Reset() callerItemList.Reset() contentRender() } //新建场景事件 newSceneBtn.OnTapped = func() { newSceneWin := dialog.NewForm("新建场景", "确定", "取消", sceneItems, func(b bool) { if !b { return } scenes_conf := mw.config.Scenes scenes_conf = append(scenes_conf, &storage.Scene{ ID: cast.ToString(time.Now().Nanosecond()), Name: sceneNameEntry.Text, Desc: sceneDescEntry.Text, }) mw.config.Scenes = scenes_conf if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } sceneFormReset() contentRender() }, mw.w) newSceneWin.Resize(fyne.NewSize(300, 200)) newSceneWin.Show() } // 编辑场景事件 editSceneBtn.OnTapped = func() { selId := sceneItemList.SelItemId if selId == "" { ShowTip("请选择一个场景") return } // 加载 var sceneConf *storage.Scene for _, item := range mw.config.Scenes { if item.ID == selId { sceneConf = item } } if sceneConf != nil { sceneNameEntry.Text = sceneConf.Name sceneDescEntry.Text = sceneConf.Desc } editSceneWin := dialog.NewForm("编辑场景", "确定", "取消", sceneItems, func(b bool) { if !b { return } for _, item := range mw.config.Scenes { if item.ID == selId { item.Name = sceneNameEntry.Text item.Desc = sceneDescEntry.Text } } if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } sceneFormReset() // contentRender() }, mw.w) editSceneWin.Resize(fyne.NewSize(300, 200)) editSceneWin.Show() } // 删除场景事件 deleSceneBtn.OnTapped = func() { selId := sceneItemList.SelectedId() if selId == "" { ShowTip("请选择一个场景") return } scenes := mw.config.Scenes for i, item := range scenes { if item.ID == selId { if len(item.Callers) > 0 { dialog.ShowCustomConfirm("提示", "确定", "取消", widget.NewLabel("该场景下存在用例,确定删除场景【"+item.Name+"】吗?"), func(b bool) { if !b { return } for y, v := range scenes { if v.ID == selId { scenes = append(scenes[:y], scenes[y+1:]...) } } mw.config.Scenes = scenes callerItemList.Reset() contentRender() if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } }, mw.w) } else { scenes = append(scenes[:i], scenes[i+1:]...) mw.config.Scenes = scenes callerItemList.Reset() contentRender() } if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } } } } // 启用场景 enableSceneBtn.OnTapped = func() { selId := sceneItemList.SelectedId() if selId == "" { ShowTip("请选择一个场景") return } for _, item := range mw.config.Scenes { if item.ID == selId { if len(item.Callers) == 0 { ShowTip("请至少添加一个用例") return } item.Status = lib.STATUS_ENABLE } else { item.Status = lib.STATUS_DISENABLE } } if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } contentRender() } //添加用例事件 newCallerBtn.OnTapped = func() { selId := sceneItemList.SelectedId() if selId == "" { ShowTip("请选择一个场景") return } //form callerForm := widget.NewForm(callerItems...) callerNameEntry.Disable() callerKeyEntry.Disable() callerForm.OnSubmit = func() { scenes_conf := mw.config.Scenes for _, v := range scenes_conf { if selId == v.ID { v.Callers = append(v.Callers, &storage.Caller{ ID: fmt.Sprintf("%s_%d", v.ID, time.Now().UnixNano()), Name: callerNameEntry.Text, Key: callerKeyEntry.Text, Num: cast.ToInt(callerNumEntry.Text), }) } } mw.config.Scenes = scenes_conf if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } callerFormReset() callerLoad(selId) } callerForm.SubmitText = "确定" //list registerCallerList := *lib.NewItemList() registerCallerList.OnSelFunc = func(id widget.ListItemID) { data := registerCallerList.CachedList.Items[id].Data info, ok := data.(lib.SceneInfo) if !ok { return } callerNameEntry.Text = info.Desc callerKeyEntry.Text = info.Name callerForm.Refresh() } registerCallerList.ListWidget = registerCallerList.CreateList() registerCallerReload := func() { for _, v := range mw.UIImpl.scenes { info := v.Info() item := lib.Item{ Id: info.Name, Text: fmt.Sprintf("%s (%s)", info.Desc, info.Name), Data: info, } registerCallerList.AddItem(item) } registerCallerList.SearchItem = registerCallerList.CachedList.Items } registerCallerReload() //search searchEntry := widget.NewEntry() searchEntry.PlaceHolder = "输入要搜索的用例" searchEntry.OnChanged = func(s string) { if s == "" { registerCallerReload() } else { newList := []lib.Item{} for _, v := range registerCallerList.SearchItem { if strings.Contains(v.Text, s) { newList = append(newList, v) } } registerCallerList.CachedList.Items = newList registerCallerList.ListWidget.Refresh() } } // layout contentSplit := container.NewHSplit( container.NewBorder(container.NewBorder(nil, nil, nil, nil, searchEntry), nil, nil, nil, registerCallerList.ListWidget), callerForm, ) contentSplit.Offset = 0.5 title := fmt.Sprintf("新建用例 - %s", selSceneName) newCallerWin := dialog.NewCustom(title, "关闭", contentSplit, mw.w) newCallerWin.Resize(fyne.NewSize(800, 600)) newCallerWin.Show() } // 删除用例事件 deleCallerBtn.OnTapped = func() { selSceneId := sceneItemList.SelectedId() if selSceneId == "" { ShowTip("请选择一个场景") return } selCallerId := callerItemList.SelectedId() if selCallerId == "" { ShowTip("请选择一个用例") return } scenes := mw.config.Scenes for _, item := range scenes { if item.ID == selSceneId { for y, c := range item.Callers { if c.ID == selCallerId { item.Callers = append(item.Callers[:y], item.Callers[y+1:]...) } } } } mw.config.Scenes = scenes if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } callerLoad(selSceneId) } } // 新场景 func (mw *MainWindow) newSceneContainer() { //注册的场景列表 registerSceneList := *lib.NewItemList() // registerSceneList.OnSelFunc = func(id widget.ListItemID) { // data := registerSceneList.CachedList.Items[id].Data // info, ok := data.(lib.SceneInfo) // if !ok { // return // } // logrus.Debug(info) // // callerNameEntry.Text = info.Desc // // callerKeyEntry.Text = info.Name // // callerForm.Refresh() // } registerSceneList.ListWidget = registerSceneList.CreateList() registerSceneReload := func() { registerSceneList.Reset() for _, v := range mw.UIImpl.scenes { info := v.Info() item := lib.Item{ Id: info.Name, Text: fmt.Sprintf("【%s】 %s", info.Name, info.Desc), Data: info, } registerSceneList.AddItem(item) } registerSceneList.SearchItem = registerSceneList.CachedList.Items } // 选中的场景列表 selectedSceneList := *lib.NewItemList() selectedSceneList.ListWidget = selectedSceneList.CreateList() selectedSceneLoad := func() { selectedSceneList.Reset() scenes := mw.config.Scenes sort.SliceStable(scenes, func(i, j int) bool { return scenes[i].Num < scenes[j].Num }) for _, v := range scenes { item := lib.Item{ Id: v.ID, Text: fmt.Sprintf("%d【%s】 %s", v.Num, v.Name, v.Desc), } selectedSceneList.AddItem(item) } } //场景按钮 addSceneBtn := widget.NewButtonWithIcon("添加场景", theme.ContentAddIcon(), nil) deleSceneBtn := widget.NewButtonWithIcon("删除场景", theme.DeleteIcon(), nil) setBtn := widget.NewButtonWithIcon("场景配置", theme.ListIcon(), nil) //search searchEntry := widget.NewEntry() searchEntry.PlaceHolder = "输入要搜索的用例" searchEntry.OnChanged = func(s string) { if s == "" { registerSceneReload() } else { newList := []lib.Item{} for _, v := range registerSceneList.SearchItem { if strings.Contains(v.Text, s) { newList = append(newList, v) } } registerSceneList.CachedList.Items = newList registerSceneList.ListWidget.Refresh() } } //布局 var dynamic fyne.CanvasObject contentRender := func() { registerSceneReload() if len(registerSceneList.CachedList.Items) == 0 { dynamic = container.NewCenter(widget.NewLabel("还没有注册任何场景")) } else { selectedSceneLoad() if len(selectedSceneList.CachedList.Items) == 0 { dynamic = container.NewCenter(widget.NewLabel("还没有选择任何场景")) } else { dynamic = container.NewBorder(container.NewHBox(setBtn, deleSceneBtn, layout.NewSpacer()), nil, nil, nil, selectedSceneList.ListWidget) } } split := container.NewHSplit( container.NewBorder( container.NewBorder(nil, nil, container.NewHBox(addSceneBtn), nil, searchEntry), nil, nil, nil, registerSceneList.ListWidget), dynamic) split.Offset = 0.5 mw.changeContent(split) } contentRender() getMaxNum := func() int { scenes := mw.config.Scenes if len(scenes) == 0 { return 1 } // 降序 sort.SliceStable(scenes, func(i, j int) bool { return scenes[i].Num > scenes[j].Num }) return scenes[0].Num + 1 } // 添加场景事件 addSceneBtn.OnTapped = func() { selId := registerSceneList.SelectedId() if selId == "" { ShowTip("请选择一个场景") return } for _, v := range mw.UIImpl.scenes { si := v.Info() if si.Name == "" { break } if si.Name == selId { item := lib.Item{ Id: si.Name, Text: si.Name, } selectedSceneList.AddItem(item) scenes_conf := mw.config.Scenes scenes_conf = append(scenes_conf, &storage.Scene{ ID: cast.ToString(time.Now().Nanosecond()), Name: si.Name, Desc: si.Desc, Num: getMaxNum(), }) mw.config.Scenes = scenes_conf if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } } } contentRender() } // 删除场景事件 deleSceneBtn.OnTapped = func() { selId := selectedSceneList.SelectedId() if selId == "" { ShowTip("请选择一个场景") return } scenes := mw.config.Scenes for i, item := range scenes { if item.ID == selId { scenes = append(scenes[:i], scenes[i+1:]...) break } } mw.config.Scenes = scenes selectedSceneLoad() if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } } // 场景配置事件 setBtn.OnTapped = func() { selId := selectedSceneList.SelectedId() if selId == "" { ShowTip("请选择一个场景") return } sceneNumEntry := widget.NewEntry() sceneNumEntry.Text = "0" sceneLoopEntry := widget.NewEntry() sceneLoopEntry.Text = "1" sceneItems := []*widget.FormItem{ widget.NewFormItem("顺序号", sceneNumEntry), widget.NewFormItem("执行次数", sceneLoopEntry), } // 加载 var sceneConf *storage.Scene for _, item := range mw.config.Scenes { if item.ID == selId { sceneConf = item } } if sceneConf != nil { sceneNumEntry.Text = cast.ToString(sceneConf.Num) if sceneConf.Loop == 0 { sceneLoopEntry.Text = "1" } else { sceneLoopEntry.Text = cast.ToString(sceneConf.Loop) } } editSceneWin := dialog.NewForm("编辑场景序号", "确定", "取消", sceneItems, func(b bool) { if !b { return } if cast.ToInt(sceneNumEntry.Text) < 0 { ShowTip("顺序号必须大于0") return } for _, item := range mw.config.Scenes { if item.ID == selId { item.Num = cast.ToInt(sceneNumEntry.Text) item.Loop = cast.ToInt32(sceneLoopEntry.Text) } } if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } selectedSceneLoad() }, mw.w) editSceneWin.Resize(fyne.NewSize(300, 150)) editSceneWin.Show() } } // 导出配置 func (mw *MainWindow) ExportConfigWindow() { dirEntry := widget.NewEntry() exportItems := []*widget.FormItem{ widget.NewFormItem("保存目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() { OpenFolder(dirEntry, mw.w) }), dirEntry)), } exportWin := dialog.NewForm("导出配置", "确定", "取消", exportItems, func(b bool) { if !b { return } dirpath := dirEntry.Text logrus.WithField("目录位置", dirpath).Debug("保存目录") jsonByte, err := json.Marshal(mw.config) if err != nil { logrus.Errorln(err) return } if err := ioutil.WriteFile(filepath.Join(dirpath, "config.json"), jsonByte, fs.ModePerm); err != nil { logrus.Error(err) return } }, mw.w) exportWin.Resize(fyne.NewSize(500, 80)) exportWin.Show() } // 导入配置 func (mw *MainWindow) ImportConfigWindow() { filepathEntry := widget.NewEntry() importItems := []*widget.FormItem{ widget.NewFormItem("配置文件", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FileIcon(), func() { OpenFile(filepathEntry, mw.w) }), filepathEntry)), } importWin := dialog.NewForm("导入配置", "确定", "取消", importItems, func(b bool) { if !b { return } filepath := filepathEntry.Text logrus.WithField("文件路径", filepath).Debug("导入文件") byte, err := ioutil.ReadFile(filepath) if err != nil { logrus.Error(err) return } if err := json.Unmarshal(byte, mw.config); err != nil { logrus.Error(err) return } if err := mw.storage.StoreConfig(mw.config); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } }, mw.w) importWin.Resize(fyne.NewSize(500, 80)) importWin.Show() } // 清空数据 func (mw *MainWindow) ClearData() { empty := &storage.Config{} mw.config = empty if err := mw.storage.StoreConfig(empty); err != nil { ShowTip(fmt.Sprintf("错误:%v", err.Error())) return } mw.startContainer() ShowTip("数据已清空") } // 退出 func (mw *MainWindow) quiteHandle() { dialog.ShowCustomConfirm("提示", "确定", "取消", widget.NewLabel("确定退出吗"), func(b bool) { if !b { return } mw.app.Quit() }, mw.w) }