dreamfactory_cmd/cmd/v2/ui/tool_term.go
2023-06-09 21:58:02 +08:00

789 lines
21 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 ui
import (
"fmt"
"go_dreamfactory/cmd/v2/lib/common"
os_storage "go_dreamfactory/cmd/v2/lib/storage"
"go_dreamfactory/cmd/v2/service"
"go_dreamfactory/cmd/v2/service/observer"
"image/color"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/pkg/errors"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"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/sirupsen/logrus"
"github.com/spf13/cast"
)
type appTerm struct {
appAdapter
obs observer.Observer
sshService *service.SSHService
jsonList *common.ItemList //json列表
cProgress *widget.ProgressBarInfinite //连接进度条进度条
upProgress *widget.ProgressBar //上传进度条
endProgress sync.WaitGroup
downloadList *common.ItemList //download列表
}
func (app *appTerm) LazyInit(ptService service.PttService, obs observer.Observer) error {
app.obs = obs
app.sshService = &service.SSHService{}
app.jsonList = common.NewItemList()
//progress
app.cProgress = widget.NewProgressBarInfinite()
app.cProgress.Hide()
app.upProgress = widget.NewProgressBar()
app.upProgress.Hide()
app.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_TERM, theme.MailSendIcon(), nil)
content := container.NewMax()
content.Objects = []fyne.CanvasObject{}
//load term conf
storage, _ := os_storage.NewOSStorage()
conf, err := storage.LoadConfig()
if err != nil {
return err
}
//left-top
localDir := widget.NewEntry()
localDir.PlaceHolder = `本地Json目录`
remoteDir := widget.NewEntry()
remoteDir.PlaceHolder = `远程目录`
remoteDir.Text = "/home/liwei/go_dreamfactory/bin/json"
//config
ip := widget.NewEntry()
ip.PlaceHolder = "10.0.0.9"
port := widget.NewEntry()
port.Text = "22" //default port
port.PlaceHolder = "端口"
userName := widget.NewEntry()
userName.PlaceHolder = "账号"
password := widget.NewPasswordEntry()
password.PlaceHolder = "密码"
passwordItem := &widget.FormItem{Text: "密码:", Widget: password}
configForm := widget.NewForm(
&widget.FormItem{Text: "IP:", Widget: container.NewBorder(nil, nil, nil, port, ip)},
&widget.FormItem{Text: "用户名:", Widget: userName},
passwordItem,
widget.NewFormItem("Json目录", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(localDir, toolWin.w)
}), localDir)),
widget.NewFormItem("远程目录", remoteDir),
)
configForm.Items[4].HintText = "谨慎修改远程目录"
//svn form
lubanAddr := widget.NewEntry()
lubanAddr.PlaceHolder = "Luban服务IP"
workDir := widget.NewEntry()
workDir.PlaceHolder = "工作目录"
lubanCli := widget.NewEntry()
lubanCli.PlaceHolder = "luban客户端可执行文件"
//define
define := widget.NewEntry()
define.PlaceHolder = "定义文件"
define.Text = `Defines\\__root__.xml`
dataDir := widget.NewEntry()
dataDir.PlaceHolder = "Data目录"
jsonDir := widget.NewEntry()
jsonDir.PlaceHolder = "Json目录"
svnForm := widget.NewForm(
&widget.FormItem{Text: "服务地址", Widget: lubanAddr},
&widget.FormItem{Text: "工作目录", Widget: container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFolder(workDir, toolWin.w)
}), workDir)},
&widget.FormItem{Text: "LubanCli", Widget: lubanCli},
&widget.FormItem{Text: "Data目录", Widget: dataDir},
&widget.FormItem{Text: "JSON目录", Widget: jsonDir},
)
//set input entry
if conf.SyncConf != nil {
ip.Text = conf.SyncConf.Ip
port.Text = conf.SyncConf.Port
userName.Text = conf.SyncConf.UserName
password.Text = conf.SyncConf.Password
localDir.Text = conf.SyncConf.LocalDir
remoteDir.Text = conf.SyncConf.RemoteDir
// //
lubanAddr.Text = conf.SyncConf.ServerIp
workDir.Text = conf.SyncConf.WorkDir
lubanCli.Text = conf.SyncConf.LubanCli
dataDir.Text = conf.SyncConf.DataDir
jsonDir.Text = conf.SyncConf.JsonDir
} else {
conf.SyncConf = &os_storage.SyncConfig{
Ip: "10.0.0.9",
UserName: "root",
Password: "Legu.cc()123",
RemoteDir: "/home/liwei/go_dreamfactory/bin/json/",
ServerIp: "10.0.1.11",
LubanCli: `Luban.Client\Luban.Client.exe`,
DataDir: "Datas",
JsonDir: `output\json`,
}
}
// 解决文本没显示的问题
passwordItem.Widget.Refresh()
ip.Refresh()
localDir.Refresh()
workDir.Refresh()
// save func
saveFunc := func() {
syncConf := &os_storage.SyncConfig{
Ip: ip.Text,
UserName: userName.Text,
Password: password.Text,
Port: port.Text,
LocalDir: localDir.Text,
RemoteDir: remoteDir.Text,
ServerIp: lubanAddr.Text,
WorkDir: workDir.Text,
LubanCli: lubanCli.Text,
DataDir: dataDir.Text,
JsonDir: jsonDir.Text,
}
conf.SyncConf = syncConf
if err := storage.StoreConfig(conf); err != nil {
logrus.Error(err)
}
}
saveBtn1 := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), saveFunc)
saveBtn2 := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), saveFunc)
// conn func
connBtn := &widget.Button{Text: "连接", Icon: theme.MailForwardIcon()}
disConnBtn := &widget.Button{Text: "断开", Icon: theme.CancelIcon()}
syncBtn := &widget.Button{Text: "同步JSON", Icon: theme.ConfirmIcon()}
refreshBtn := &widget.Button{Text: "刷新", Icon: theme.ViewRefreshIcon()}
svnBtn := &widget.Button{Text: "SVN更新", Icon: theme.ConfirmIcon()}
excelBtn := &widget.Button{Text: "更新Excel", Icon: theme.FolderIcon()}
explorBtn := &widget.Button{Text: "资源管理器", Icon: theme.FolderIcon()}
dlBtn := widget.NewButtonWithIcon("", theme.DownloadIcon(), nil)
searchEntry := widget.NewEntry()
// 全选/全取消
allSelBtn := &widget.Button{Icon: theme.CheckButtonCheckedIcon()}
allCancelBtn := &widget.Button{Icon: theme.CheckButtonIcon()}
allSelBtn.Hide()
// 连接
connBtn.OnTapped = func() {
app.cProgress.Show()
app.cProgress.Start()
ciphers := []string{}
if ip.Text == "" {
dialog.ShowError(errors.New("IP未填写"), toolWin.w)
return
}
if userName.Text == "" {
dialog.ShowError(errors.New("账号未填写"), toolWin.w)
return
}
if password.Text == "" {
dialog.ShowError(errors.New("密码未填写"), toolWin.w)
return
}
if port.Text == "" {
dialog.ShowError(errors.New("端口未填写"), toolWin.w)
return
}
if localDir.Text == "" {
dialog.ShowError(errors.New("JSON目录未填写"), toolWin.w)
return
}
if remoteDir.Text == "" {
dialog.ShowError(errors.New("远程目录未填写"), toolWin.w)
return
}
connBtn.Disable()
err := app.sshService.Connect(userName.Text, password.Text, ip.Text, "", cast.ToInt(port.Text), ciphers)
if err != nil {
dialog.ShowError(err, toolWin.w)
app.cProgress.Stop()
app.cProgress.Hide()
connBtn.Enable()
return
} else {
app.jsonList.LoadItem(localDir.Text)
app.cProgress.Stop()
app.cProgress.Hide()
disConnBtn.Enable()
syncBtn.Enable()
app.upProgress.Hide()
allCancelBtn.Show()
allSelBtn.Hide()
refreshBtn.Enable()
dlBtn.Enable()
}
}
// 刷新JSON列表
refreshBtn.Disable()
reloadItem := func() {
defer func() {
syncBtn.Enable()
allSelBtn.Hide()
allCancelBtn.Show()
}()
app.jsonList.LoadItem(localDir.Text)
}
refreshBtn.OnTapped = reloadItem
// 断开
disConnBtn.Disable()
disConnBtn.OnTapped = func() {
defer func() {
app.jsonList.Reset()
connBtn.Enable()
disConnBtn.Disable()
syncBtn.Disable()
allSelBtn.Hide()
allCancelBtn.Show()
refreshBtn.Disable()
dlBtn.Disable()
}()
app.sshService.Close()
}
//资源管理器
openExplor := func(dir string) {
if runtime.GOOS == "windows" {
if err := exec.Command("explorer", filepath.Join(dir)).Start(); err != nil {
dialog.ShowError(errors.WithMessage(err, "请确认Json目录是否填写正确"), toolWin.w)
return
}
}
}
explorBtn.OnTapped = func() {
// logrus.Debug(localDir.Text)
if localDir.Text == "" {
common.ShowTip("Json目录必须填写")
} else {
openExplor(localDir.Text)
}
}
//使用说明
helpBtn1 := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
quesWin := fyne.CurrentApp().NewWindow("同步JSON使用说明")
quesWin.SetContent(widget.NewRichTextFromMarkdown(
`
* 同步Json的操作仅限于数值热更,非结构热更
* 将json文件热更到内网测试环境
* 提示:
* IP:10.0.0.9 22 (保持一致)
* 用户名:root(保持一致)
* 密码:(服务端同学提供)
* Json目录:(json文件目录全路径)
* 远程目录:/home/liwei/go_dreamfactory/bin/json/(保持一致)
* 全部填写完点击【保存配置】
* 点击【连接】,右侧面板选择要同步的文件
`))
quesWin.Resize(fyne.NewSize(350, 200))
quesWin.SetFixedSize(true)
quesWin.CenterOnScreen()
quesWin.Show()
})
helpBtn2 := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
quesWin := fyne.CurrentApp().NewWindow("SVN更新使用说明")
c := widget.NewRichTextFromMarkdown(
`
* 基于Luban工具生成json
* 建议每次同步JSON之前先点击SVN更新
* 提示:
* 服务地址:10.0.1.11 (保持一致)
* 工作目录:svn下ExcelFile目录全路径
* LubanCli:Luban.Client\Luban.Client.exe (保持一致)
* Data目录:Datas(保持一致)
* JSON目录:output\json(保持一致)
* 全部填写完点击【保存配置】
* 点击【SVN更新】生成json文件
`,
)
quesWin.SetContent(c)
quesWin.Resize(fyne.NewSize(350, 200))
quesWin.SetFixedSize(true)
quesWin.CenterOnScreen()
quesWin.Show()
})
//同步JSON
syncBtn.Disable()
syncNext := func() {
defer func() {
syncBtn.Enable()
app.upProgress.Hide()
reloadItem()
dialog.ShowInformation("提示", "所有文件均上传完毕,需等1-2分钟待文件热更", toolWin.w)
}()
syncBtn.Disable()
app.upProgress.Show()
app.upProgress.SetValue(0)
len := len(app.jsonList.SelItemIds)
num := 0.0
increment := func(wg *sync.WaitGroup) {
num += float64(1) / float64(len)
app.upProgress.SetValue(num)
wg.Done()
}
for _, fileName := range app.jsonList.SelItemIds {
app.endProgress.Add(1)
go func(fn string) {
// time.Sleep(time.Second * time.Duration(rand.Intn(3)))
if err := app.sshService.ScpCopy(filepath.Join(localDir.Text, fn), remoteDir.Text); err != nil {
logrus.WithField("err", err).Error("同步json")
common.ShowTip(err.Error())
}
increment(&app.endProgress)
// 移除已上传的
app.jsonList.DeleteItem(fn)
common.ShowTip(fmt.Sprintf("%s 成功上传", fn))
}(fileName)
}
app.endProgress.Wait()
app.upProgress.SetValue(1)
}
syncBtn.OnTapped = func() {
if app.sshService.Client == nil {
dialog.ShowError(errors.New("请先连接终端"), toolWin.w)
return
}
//SVN更新提示
dc := dialog.NewConfirm("提示", "是否要进行SVN更新", func(b bool) {
if b {
common.ShowTip("单击【SVN更新】按钮进行更新")
svnBtn.FocusGained()
return
} else {
if len(app.jsonList.SelItemIds) == 0 {
common.ShowTip("没有选择任何文件,或尝试点击【刷新】")
return
}
syncNext()
}
}, toolWin.w)
dc.SetConfirmText("必须的")
dc.SetDismissText("我拿生命担保无需更新")
dc.Show()
}
// excel更新
excelBtn.OnTapped = func() {
if workDir.Text == "" {
common.ShowTip("工作目录必须填写")
} else {
openExplor(workDir.Text)
}
}
// SVN更新
svnNext := func() {
defer func() {
app.cProgress.Hide()
app.cProgress.Stop()
svnBtn.Enable()
}()
svnBtn.Disable()
app.cProgress.Show()
app.cProgress.Start()
commandStr := `%s -h %s -j cfg -- -d %s --input_data_dir %s --output_data_dir %s --gen_types data_json -s server`
arg := fmt.Sprintf(commandStr,
filepath.Join(workDir.Text, lubanCli.Text),
lubanAddr.Text,
filepath.Join(workDir.Text, define.Text),
filepath.Join(workDir.Text, dataDir.Text),
filepath.Join(workDir.Text, jsonDir.Text),
)
logrus.Debug(arg)
c := exec.Command("cmd.exe", "/c", arg)
if err := c.Run(); err != nil {
dialog.ShowError(err, toolWin.w)
return
}
}
svnBtn.OnTapped = func() {
//提示更新excel
dc := dialog.NewConfirm("提示", "是否要更新excel?", func(b bool) {
if b {
//打开资源管理器
openExplor(workDir.Text)
return
} else {
svnNext()
}
}, toolWin.w)
dc.SetConfirmText("必须的")
dc.SetDismissText("我拿生命担保无需更新")
dc.Show()
}
// 全部取消
allCancelBtn.OnTapped = func() {
defer func() {
allCancelBtn.Hide()
allSelBtn.Show()
}()
for i, v := range app.jsonList.CachedList.Items {
app.jsonList.CachedList.Items[i].Checked = true
app.jsonList.SelItemIds = append(app.jsonList.SelItemIds, v.Text)
app.jsonList.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
}
app.jsonList.ItemList.Refresh()
}
// 全选
allSelBtn.OnTapped = func() {
defer func() {
allCancelBtn.Show()
allSelBtn.Hide()
}()
app.jsonList.SelItemIds = []string{}
for i, v := range app.jsonList.CachedList.Items {
app.jsonList.CachedList.Items[i].Checked = false
app.jsonList.ItemList.UpdateItem(i, widget.NewCheck(v.Text, nil))
}
app.jsonList.ItemList.Refresh()
}
// 搜索
searchEntry.PlaceHolder = "搜索"
searchEntry.OnChanged = func(s string) {
if app.sshService.Client == nil {
dialog.ShowError(errors.New("请先连接终端"), toolWin.w)
return
}
if s == "" {
reloadItem()
} else {
// go func() {
newList := []common.Item{}
for _, v := range app.jsonList.SearchItem {
if strings.Contains(v.Text, s) {
newList = append(newList, v)
}
}
app.jsonList.CachedList.Items = newList
app.jsonList.ItemList.Refresh()
// }()
}
}
// 下载日志
dlBtn.Disable()
dlBtn.OnTapped = func() {
w := app.createDownloadWindow()
w.Show()
w.SetCloseIntercept(func() {
dlBtn.Enable()
w.Close()
})
dlBtn.Disable()
}
// 创建json列表
app.jsonList.ItemList = app.jsonList.CreateDefaultCheckList()
btns1 := container.NewHBox(helpBtn1, dlBtn, &layout.Spacer{}, saveBtn1, connBtn, disConnBtn)
btns2 := container.NewHBox(helpBtn2, &layout.Spacer{}, saveBtn2, excelBtn, svnBtn)
split := container.NewHSplit(container.NewVBox(canvas.NewText("---只能在非上产环境!!!同步Json的操作仅限于数值热更,非结构热更---", color.RGBA{255, 0, 0, 255}), configForm,
btns1, svnForm, btns2, app.cProgress), container.NewBorder(
container.NewBorder(nil, nil, container.NewHBox(allCancelBtn, allSelBtn, syncBtn, refreshBtn), container.NewHBox(explorBtn), searchEntry),
nil, nil, nil, app.jsonList.ItemList))
split.Offset = 0.45
content.Objects = append(content.Objects, container.NewBorder(nil, app.upProgress, nil, nil, split))
app.tabItem.Content = content
return nil
}
func (a *appTerm) GetAppName() string {
return common.TOOLBAR_TERM
}
func (a *appTerm) Icon() fyne.Resource {
return theme.MailSendIcon()
}
func OpenExplor(dir string) {
if dir == "" {
common.ShowTip("资源管理器路径错误")
return
}
if runtime.GOOS == "windows" {
if err := exec.Command("explorer", filepath.Join(dir)).Start(); err != nil {
dialog.ShowError(errors.WithMessage(err, "请确认Json目录是否填写正确"), toolWin.w)
return
}
}
}
func (a *appTerm) OnClose() bool {
dialog.ShowConfirm("关闭终端", "你希望断开连接吗?", func(b bool) {
if !b {
return
}
if a.sshService.Client != nil {
a.sshService.Client.Close()
}
}, toolWin.w)
return true
}
func (a *appTerm) createDownloadWindow() fyne.Window {
w := toolWin.app.NewWindow("日志")
a.downloadList = common.NewItemList()
a.downloadList.ItemList = a.downloadList.CreateDownloadList()
remoteLogDirEntry := widget.NewEntry()
remoteLogDirEntry.PlaceHolder = "下载到"
remoteLogDirEntry.Text = "/home/liwei/go_dreamfactory/bin/log/"
saveDirEntry := widget.NewEntry()
saveDirEntry.PlaceHolder = "保存目录"
editorEntry := widget.NewEntry()
editorEntry.PlaceHolder = "编辑器可执行文件路径"
// config
confForm := widget.NewForm(
widget.NewFormItem("远程目录", remoteLogDirEntry),
widget.NewFormItem("下载目录", saveDirEntry),
widget.NewFormItem("编辑器", container.NewBorder(nil, nil, nil, widget.NewButtonWithIcon("", theme.FolderIcon(), func() {
openFile(editorEntry, w)
}), editorEntry)),
)
// 进度条
downloadProgress := widget.NewProgressBarInfinite()
var (
storage os_storage.Storage
conf *os_storage.Config
err error
)
defer func() {
downloadProgress.Hide()
//加载配置
storage, _ = os_storage.NewOSStorage()
conf, err = storage.LoadConfig()
if err != nil {
return
}
// sshConf = service.GetDbService().GetSSHConf(common.BUCKET_SSHCONF)
if conf.SyncConf != nil {
if conf.SyncConf.LogDir == "" {
remoteLogDirEntry.Text = "/home/liwei/go_dreamfactory/bin/log/"
} else {
remoteLogDirEntry.Text = conf.SyncConf.LogDir
}
saveDirEntry.Text = conf.SyncConf.SaveDir
editorEntry.Text = conf.SyncConf.Editor
} else {
conf.SyncConf = &os_storage.SyncConfig{}
}
remoteLogDirEntry.Refresh()
saveDirEntry.Refresh()
editorEntry.Refresh()
//load list
if remoteLogDirEntry.Text == "" {
return
}
files, err := a.sshService.GetRemoteDir(remoteLogDirEntry.Text)
if err != nil {
dialog.ShowError(err, toolWin.w)
return
}
for _, f := range files {
a.downloadList.AddItem(common.Item{Id: f.FileName, Text: f.FileName, Size: f.Size})
}
}()
//下载
allDownloadBtn := widget.NewButtonWithIcon("下载", theme.DownloadIcon(), nil)
DownloadDirBtn := widget.NewButtonWithIcon("打开下载目录", theme.FolderIcon(), nil)
EditorBtn := widget.NewButtonWithIcon("打开编辑器", theme.FolderIcon(), nil)
saveConfBtn := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), func() {
// 保存配置
conf.SyncConf.LogDir = remoteLogDirEntry.Text
conf.SyncConf.SaveDir = saveDirEntry.Text
conf.SyncConf.Editor = editorEntry.Text
if err := storage.StoreConfig(conf); err != nil {
logrus.Error(err)
}
})
allSelBtn := &widget.Button{Icon: theme.CheckButtonCheckedIcon()}
allCancelBtn := &widget.Button{Icon: theme.CheckButtonIcon()}
allSelBtn.Hide()
// 全部取消
allCancelBtn.OnTapped = func() {
defer func() {
allCancelBtn.Hide()
allSelBtn.Show()
}()
dlist := a.downloadList
for i, v := range dlist.CachedList.Items {
dlist.CachedList.Items[i].Checked = true
dlist.SelItemIds = append(dlist.SelItemIds, v.Text)
dlist.ItemList.UpdateItem(i, container.NewHBox(
widget.NewCheck(v.Text, nil),
&layout.Spacer{},
widget.NewLabel(common.ConvertFileSize(v.Size))))
}
dlist.ItemList.Refresh()
}
// 全选
allSelBtn.OnTapped = func() {
defer func() {
allCancelBtn.Show()
allSelBtn.Hide()
}()
dlist := a.downloadList
dlist.SelItemIds = []string{}
for i, v := range dlist.CachedList.Items {
dlist.CachedList.Items[i].Checked = false
dlist.ItemList.UpdateItem(i, container.NewHBox(
widget.NewCheck(v.Text, nil),
&layout.Spacer{},
widget.NewLabel(common.ConvertFileSize(v.Size))))
}
dlist.ItemList.Refresh()
}
// 打开下载目录
DownloadDirBtn.OnTapped = func() {
OpenExplor(saveDirEntry.Text)
}
EditorBtn.OnTapped = func() {
if editorEntry.Text == "" {
common.ShowTip("请配置编辑器")
return
}
if runtime.GOOS == "windows" {
for _, v := range a.downloadList.SelItemIds {
if err := exec.Command(editorEntry.Text, filepath.Join(saveDirEntry.Text, v)).Start(); err != nil {
dialog.ShowError(errors.WithMessage(err, "请确认编辑器目录是否填写正确"), toolWin.w)
return
}
}
}
}
//使用说明
helpBtn := widget.NewButtonWithIcon("", theme.QuestionIcon(), func() {
quesWin := fyne.CurrentApp().NewWindow("日志使用说明")
quesWin.SetContent(widget.NewRichTextFromMarkdown(
`
* 下载远程服务日志到本地分析
* 提示:
* 远程目录 (保持一致)
* 下载目录:填写本地任意目录
* 编辑器:选择已安装的编辑器(例如:notepad++、Sublime)
* 操作:
* 选择文件,点击【打开编辑器】
`))
quesWin.Resize(fyne.NewSize(350, 200))
quesWin.SetFixedSize(true)
quesWin.CenterOnScreen()
quesWin.Show()
})
var wg sync.WaitGroup
//download
allDownloadBtn.OnTapped = func() {
downloadProgress.Show()
downloadProgress.Start()
selItems := a.downloadList.SelItemIds
if len(selItems) == 0 {
common.ShowTip("请选择下载的文件")
return
} else {
for _, item := range selItems {
wg.Add(1)
go func(name string) {
defer func() {
downloadProgress.Hide()
wg.Done()
}()
logrus.WithField("filepath", saveDirEntry.Text+name).Debug("下载")
if err := a.sshService.ScpDownload(saveDirEntry.Text, remoteLogDirEntry.Text+name); err != nil {
common.ShowTip(name + " 下载失败")
}
}(item)
}
wg.Wait()
}
}
btns := container.NewHBox(allSelBtn, allCancelBtn, allDownloadBtn, DownloadDirBtn, EditorBtn, helpBtn)
//toolbar
toolbar := container.NewBorder(nil, btns, nil, saveConfBtn, confForm)
//layout
w.SetContent(container.NewBorder(toolbar, downloadProgress, nil, nil, a.downloadList.ItemList))
w.Resize(fyne.NewSize(800, 450))
w.CenterOnScreen()
return w
}