golang跨平台GUI框架fyne介绍与使用详解
Fyne 是一个使用 Go 编写的易于使用的 UI 工具包和应用程序 API。 它旨在构建使用单一代码库在桌面和移动设备上运行的应用程序。
通过批量调用身份证实名和三网手机实名和银行卡核验等接口,完成fyne框架的基本使用介绍
主要包括
- fyne框架的基本应用
- fyne框架的自定义字体使用
- 通过go的并发协程方式实现同时处理多任务
福利彩蛋:没有好玩的 API 接口?上百款免费接口等你来,免费 API,免费 API 大全
介绍
这是一款本地批量核验小工具,测试接口示例来源聚合数据。
计划支持:showapi、阿里云等
该应用集成了身份证实名、三网手机实名和银行卡二、三、四元素校验等多种核验方式;
旨在帮助有需求但无技术的用户快速、准确地完成身份验证,提升用户体验和工作效率;
让实名认证、银行卡核验变得简单、高效!
有技术支持的用户,可以选择接口调用
特点
- 身份证实名认证:通过姓名和身份证号码,快速验证身份证的真实性和一致性,确保您的身份信息安全。
- 三网手机实名认证:核验手机运营商三要素(手机号码、姓名、身份证号)信息是否一致。
- 银行卡二、三、四元素校验:姓名、身份证号码、银行卡号、手机号等相关信息,快速验证银行卡的真实性和一致性,保障您的资金安全。
- 本地批量处理:通过模板上传数据,批量进行核验
- 用户友好界面:简洁直观的操作界面,让您轻松上手,快速完成信息核验。
应用下载:本地批量核验工具
上代码
1.初始化应用
package mainimport ("fyne_study/global""fyne_study/model""fyne_study/mytheme""fyne_study/widgets""fyne.io/fyne/v2""fyne.io/fyne/v2/app""fyne.io/fyne/v2/container"
)func main() {global.AppRoot = app.NewWithID("cn.juhe.preferences.v1.0")global.AppRoot.Settings().SetTheme(&mytheme.MyTheme{})global.W = global.AppRoot.NewWindow("身份证、手机号、银行卡批量核验工具")global.W.Resize(fyne.NewSize(1200, 820))if !model.CheckTable() {model.InitDatabase()global.InfoDialog("数据库初始化结束")}// 设置主菜单:mainmenu是桌面应用上的菜单// global.W.SetMainMenu(widgets.MenuList())header := container.NewVBox(widgets.Row1(), widgets.Row2())// header := container.NewVBox(widget.NewLabel("databalse"))ly := container.NewBorder(header, nil, nil, nil, widgets.Row3())global.W.SetContent(ly)global.W.ShowAndRun()
}
2.menu 属性菜单
package widgetsimport ("fyne_study/global""fyne.io/fyne/v2""fyne.io/fyne/v2/container""fyne.io/fyne/v2/dialog""fyne.io/fyne/v2/widget"
)func MenuList() *fyne.MainMenu {//打开菜单项openMenuItem := fyne.NewMenuItem("延迟有效期", func() {sign := widget.NewEntry()sign.SetPlaceHolder("请输入签名,延迟有效期")dialog.ShowForm("延迟有效期", "确认", "取消", []*widget.FormItem{{Text: "", Widget: container.NewGridWrap(fyne.NewSize(300, 35), sign)},}, func(b bool) {if b {if sign.Text != "" {global.LabelMsgText(sign.Text)} else {global.LabelMsgText("签名不正确")}}}, global.W)})//保存菜单项saveMenuItem := fyne.NewMenuItem("联系作者", func() {dialog.ShowInformation("联系作者","QQ:1776403827 ",global.W)})fileMenu := fyne.NewMenu("File", openMenuItem, saveMenuItem)// 表示文件菜单有三个选项menu := fyne.NewMainMenu(fileMenu)return menu}
3.接口地址和appkey选择输入,保存
示例代码框中的appkey获取
在数据中心 > 我的API获取已申请的接口Appkey
同样在这里可以申请您感兴趣的接口
- [208]三网手机实名制认证[简]
- [103]身份证实名认证
- [188]银行卡姓名二要素核验
- [762]银行卡身份证二要素核验
- [763]银行卡手机号二要素核验
- [207]银行卡三要素核验[简]
- [213]银行卡四要素核验[简]
将申请接的接口的Key黏贴到对应的接口位置
每个接口仅第一次使用时需要配置并保存一次key
选择核验并发次数
选择对应的接口后,下载上传模板,严格按照模板格式上传
上传完数据后,点击开始
完成后先导出数据
再清空数据列表,进行下一批次核验
package widgetsimport ("fmt""fyne_study/global""fyne_study/utils""net/url""time""fyne.io/fyne/v2""fyne.io/fyne/v2/container""fyne.io/fyne/v2/data/binding""fyne.io/fyne/v2/dialog""fyne.io/fyne/v2/theme""fyne.io/fyne/v2/widget"
)func Row1() *fyne.Container {// 重新启动是选择上次的结果api := global.GetCurrentApi()// 接口选择global.ApiSelect = widget.NewSelect(global.ApiList, nil)global.ApiSelect.OnChanged = func(api string) {_api := global.GetCurrentApi()if global.Total > 0 && api != _api {global.ErrorDialog("请求处理完并导出已有数据并清空表单,然后切换接口")global.ApiSelect.SetSelected(_api)return}// 保存选择的Api接口global.SetPreferenceVal("selectedApi", "", "", api)// 设置并发绑定数nums := global.GetPreferenceVal(api, "concurrency", "string", "5").(string)if global.PoolSelect != nil {global.PoolSelect.SetSelected(nums)}// appkey处理keyStr := global.GetPreferenceVal(api, "key", "string", "").(string)global.BindAppKey.Set(keyStr)fmt.Println("appkey:", keyStr)if keyStr == "" {global.LabelMsgText("当前任务:" + api + ";AppKey:未设置Key")} else {global.LabelMsgText("当前任务:" + api + ";AppKey:" + keyStr + ";并发数:" + nums)}if global.ListTitle != nil {global.GetApiInfoSlice(api, "colums")global.CurCols = global.GetApiInfoSlice(api, "colums")global.CurRxp = global.GetApiInfoStr(api, "codeExp")_cols := global.GetApiInfoSlice(_api, "colums")diff := len(_cols) - len(global.CurCols)// VBox->GridWarp->Labelgrids := global.ListTitle.Objectsfor i := 0; i < len(global.CurCols); i++ {grids[i].(*fyne.Container).Objects[0].(*widget.Label).SetText(global.CurCols[i])}if diff > 0 {// 列数减少for i := 0; i < len(_cols); i++ {if i >= len(global.CurCols) {grids[i].(*fyne.Container).Objects[0].(*widget.Label).SetText("")}}}}}// 从配置加载选中的APIglobal.ApiSelect.SetSelected(api)// appkey输入框global.AppKeyEntry = widget.NewEntryWithData(global.BindAppKey)global.AppKeyEntry.SetPlaceHolder("配置该接口的appkey")// 并发数选择框global.PoolSelect = widget.NewSelect([]string{"1", "5", "10", "15", "20" /*, "25", "30", "35", "40", "50"*/}, nil)global.PoolSelect.OnChanged = func(val string) {_nums := global.GetPreferenceVal(api, "concurrency", "string", "5").(string)// 设置并发数之前,渠道任务未开启或暂停if global.QueueStatus == "start" && _nums != val {global.ErrorDialog("请先确任务为开始或已暂停,然后在设置并发数")global.PoolSelect.SetSelected(_nums)return}global.LabelMsgText(fmt.Sprintf("接口情况:%s,当前并发:%s", global.ApiSelect.Selected, val))// 当并发数改变时,根据选择的接口进行,并发数设置global.SetPreferenceVal(global.ApiSelect.Selected, "concurrency", "string", val)}// 从配置加载并发数量nums := global.GetPreferenceVal(api, "concurrency", "string", "5").(string)global.PoolSelect.SetSelected(nums)save := widget.NewButtonWithIcon("保存配置", theme.DocumentSaveIcon(), func() {msg := "当前任务:" + apikeyStr := global.AppKeyEntry.Textglobal.SetPreferenceVal(global.ApiSelect.Selected, "key", "string", keyStr)if keyStr == "" {msg += ";AppKey:未设置Key"} else {msg += ";AppKey:" + keyStr}msg += ";并发数:" + numsglobal.LabelMsgText(msg)})// // 通过NewGridWrap实现组件尺寸自定义// rowEn := container.NewGridWrap(fyne.NewSize(280, 35), global.ApiSelect)// rowKy := container.NewGridWrap(fyne.NewSize(280, 35), global.AppKeyEntry)// rowPoL := container.NewGridWrap(fyne.NewSize(50, 35), widget.NewLabel("并发数:"))// rowPo := container.NewGridWrap(fyne.NewSize(80, 35), global.PoolSelect)// rowBt := container.NewGridWrap(fyne.NewSize(100, 35), save)left := container.NewGridWithColumns(2, global.ApiSelect, global.AppKeyEntry)right := container.NewGridWithColumns(3, widget.NewLabel("并发数:"), global.PoolSelect, save)global.LabelMsg = widget.NewLabel("操作提示:")df := time.Now().AddDate(0, 6, 0).Format(global.DateTime)expire := global.GetPreferenceVal("app-expire", "", "string", df).(string)if expire == "" {expire = dfglobal.SetPreferenceVal("app-expire", "", "string", expire)}global.Expire = binding.BindString(&expire)global.SignBtn = widget.NewButtonWithIcon("激活", theme.HistoryIcon(), func() {SignActive(expire)})if expire >= time.Now().Format(global.DateTime) {global.SignBtn.Hide()} else {global.SignBtn.Show()}_url, _ := url.Parse("https://www.juhe.cn/s/swnkequ0q5dccl=?s=utm_id55")box := container.NewHBox(widget.NewHyperlink("注册聚合账号", _url), widget.NewLabel("问题反馈:1776403827(QQ)"), global.SignBtn, widget.NewLabel("有效期:"), widget.NewLabelWithData(global.Expire))row := container.NewBorder(container.NewBorder(nil, nil, nil, box, global.LabelMsg), nil, nil, right, left)return row
}// 签名激活
func SignActive(expire string) {sign := widget.NewMultiLineEntry()sign.Wrapping = fyne.TextWrapWordsign.SetPlaceHolder("请输入激活码,激活软件")dialog.ShowForm("激活", "确认", "取消", []*widget.FormItem{{Text: "", Widget: container.NewGridWrap(fyne.NewSize(300, 75), sign)},}, func(b bool) {if b {if sign.Text != "" {tx := global.GetPreferenceVal("app-active-sing", "", "string", "").(string)if tx == sign.Text {global.ErrorDialog("激活码已使用")return}dec, err := utils.ECBDecrypt([]byte(sign.Text), global.EcbKey)if err == nil && string(dec)[:10] == global.SignKey {if err == nil {date, err := time.Parse(global.DateTime, expire)if err == nil {expire = date.AddDate(1, 0, 0).Format(global.DateTime)global.Expire.Set(expire)global.SignBtn.Hide()global.SetPreferenceVal("app-expire", "", "string", expire)global.SetPreferenceVal("app-active-sing", "", "string", sign.Text)}}}if err != nil {global.ErrorDialog("激活码不正确")return} else {global.InfoDialog("软件激活成功")return}} else {global.LabelMsgText("签名不正确")}}}, global.W)
}
4.上传需要核验的数据文件
布局一个文件上传以及模板下载区域
package widgetsimport ("fmt""fyne_study/global""fyne_study/model""strings""time""fyne.io/fyne/v2""fyne.io/fyne/v2/container""fyne.io/fyne/v2/dialog""fyne.io/fyne/v2/storage""fyne.io/fyne/v2/theme""fyne.io/fyne/v2/widget""github.com/tealeg/xlsx"
)// border : center,right
func Row2() *fyne.Container {file := widget.NewEntry()open := widget.NewButtonWithIcon("打开文件", theme.DocumentSaveIcon(), nil)// 点击打开上传文件open.OnTapped = func() {// 再次上传时候判断if global.Total > 0 {global.ErrorDialog("请求处理完并导出已有数据并清空表单,然后切换接口")return}fileDialog := dialog.NewFileOpen(func(uc fyne.URIReadCloser, err error) {if uc != nil {fmt.Println(uc.URI().Path())file.SetText(uc.URI().Path())fmt.Println("uc.URI().Extension()", uc.URI().Extension())}fmt.Println("保存配置=", err)}, global.W)fileDialog.Resize(fyne.NewSize(1000,600))fileDialog.SetFilter(storage.NewExtensionFileFilter([]string{".xlsx"}))fileDialog.Show()fmt.Println("保存配置")}// 处理进度条upload := widget.NewButtonWithIcon("上传", theme.UploadIcon(), func() {api := global.GetCurrentApi()cols := global.GetApiInfoSlice(api, "colums")dialog.ShowConfirm("提示", "确定上传该文件?"+file.Text, func(b bool) {if b && file.Text != "" && strings.Contains(file.Text, ".xlsx") {fmt.Println("正在读取:", file.Text)excel, err := xlsx.OpenFile(file.Text)if err == nil {sheet := excel.Sheets[0]// 上传数据校验if len(sheet.Cols) != (len(cols) - 4) {global.ErrorDialog(fmt.Sprintf("上传数据格式不正确:列数不匹配【%d】,请严格安照模板上传【%d】", len(sheet.Cols), (len(cols) - 4)))return}pBar := widget.NewProgressBar()pc := container.NewGridWrap(fyne.NewSize(300, 20), pBar)bar := dialog.NewCustom("提示", "上传数据处理中...", pc, global.W)bar.Show()rows, err := model.BatchInsert(sheet, pBar)if err != nil {bar.Hide()global.ErrorDialog("数据上传异常:" + err.Error())} else {bar.SetDismissText(fmt.Sprintf("上传结束:处理数据%d", rows))bar.SetOnClosed(func() {ListTabelRefresh(1, global.PageSize)fmt.Println("重置当前页码:", 1)file.SetText("")if global.QueueStatus == "end" && global.QueueBtn != nil {global.QueueBtn.SetIcon(theme.MediaPlayIcon())global.QueueBtn.SetText("开始")}})}if rows > 0 {global.ProcessBar.SetValue(0)global.ProcessBar.Show()}} else {dialog.ShowError(err, global.W)}} else if file.Text == "" || !strings.Contains(file.Text, ".xlsx") {global.ErrorDialog("请选择正确的上传文件")} else {fmt.Println("文件地址", file.Text)}}, global.W)})// 示例文件下载// download := widget.NewHyperlink("下载模板", nil)// download.OnTapped = func() {// api := global.GetCurrentApi()// exampleUrl := global.GetApiInfoStr(api, "example")// _url, _ := url.Parse(exampleUrl)// fyne.CurrentApp().OpenURL(_url)// }download := widget.NewButtonWithIcon("下载模板", theme.DocumentSaveIcon(), func() {// 获取当前选中的接口api := global.GetCurrentApi()// 打开文件保存地址fileDialog := dialog.NewFileSave(func(uc fyne.URIWriteCloser, err error) {if uc != nil {example := global.GetApiInfo2Slice(api, "example")f := xlsx.NewFile()sheet, err := f.AddSheet("Sheet1")if err != nil {global.ErrorDialog(err.Error())return}for _, item := range example {row := sheet.AddRow()for _, col := range item {cell := row.AddCell()cell.Value = col}}err = f.Save(uc.URI().Path())if err != nil {global.LabelMsg.SetText(err.Error())return}time.Sleep(time.Second * 1)uc.Close()global.InfoDialog("模板下载成功")} else {if err == nil {global.LabelMsgText("")} else {global.LabelMsgText(err.Error())}}}, global.W)fileDialog.Resize(fyne.NewSize(1000,600))fileDialog.SetFileName(api + "-tpl.xlsx")fileDialog.Show()})row := container.NewBorder(nil, nil, nil, container.NewGridWithColumns(3, open, upload, download), file)return row
}
5.渲染上传进度条,并发处理进度条,以及数据执行按钮等工具栏
渲染上传进度条,并发处理进度条,以及数据执行按钮等工具栏
核验结果的数据导出功能
package widgetsimport ("encoding/json""fmt""fyne_study/global""fyne_study/model""fyne_study/utils""strconv""strings""time""fyne.io/fyne/v2""fyne.io/fyne/v2/container""fyne.io/fyne/v2/dialog""fyne.io/fyne/v2/theme""fyne.io/fyne/v2/widget""github.com/tealeg/xlsx"
)// border : right
func Row3() *fyne.Container {label := widget.NewLabel("数据列表:")// ready,start,pause,endglobal.QueueBtn = widget.NewButtonWithIcon("开始", theme.MediaPlayIcon(), nil)global.QueueBtn.OnTapped = func() {if global.QueueBtn != nil {fmt.Println("=====", global.QueueBtn.Text)if global.QueueBtn.Text == "开始" {key, _ := global.BindAppKey.Get()if len(key) != 32 {global.InfoDialog("请先配置正确的接口请求Key")return}expire, err := global.Expire.Get()if err != nil {global.LabelMsgText(err.Error())return}if expire < time.Now().Format("2006-01-02") {global.InfoDialog("该软件使用已过有效期,请重新激活")global.SignBtn.Show()return} else {global.SignBtn.Hide()}if global.Total > 0 {if model.CompletedCount() == global.Total {global.InfoDialog("该任务已完成,请导出并清空数据,进行下一批任务")return}run := dialog.NewConfirm("提示", "确定立即开始任务?", func(e bool) {if e {global.QueueStatus = "start"global.QueueBtn.SetText("暂停")global.QueueBtn.SetIcon(theme.MediaPauseIcon())go queueRun(global.QueueBtn)}}, global.W)run.SetConfirmText("确认")run.SetDismissText("取消")run.Show()} else {global.InfoDialog("当前没有任务要执行")}} else if global.QueueBtn.Text == "暂停" {global.QueueStatus = "pause"global.QueueBtn.SetText("开始")global.QueueBtn.SetIcon(theme.MediaPlayIcon())global.LabelMsgText("当前任务已暂停")} else {global.InfoDialog("该任务已完成,请导出并清空数据,进行下一批任务")return}}}export := widget.NewButtonWithIcon("导出数据", theme.DownloadIcon(), nil)export.OnTapped = func() {// 设置并发数之前,渠道任务未开启或暂停if global.QueueStatus == "start" {global.ErrorDialog("请先确任务未开始或已暂停,然后再导出数据")return}count, err := model.UnCompletedCount()if err != nil {global.ErrorDialog(err.Error())return}// 存在未处理的数据if count > 0 {dialog.ShowConfirm("提示", "数据未处理完,是否确认导出?", func(b bool) {if b {ExportData()}}, global.W)} else {ExportData()}}// 情况数据按钮clear := widget.NewButtonWithIcon("清空数据", theme.ContentClearIcon(), func() {dialog.ShowConfirm("提示", "确认要情况当前任务数据?", func(b bool) {if b {// 设置并发数之前,渠道任务未开启或暂停if global.QueueStatus == "start" {global.ErrorDialog("请先确任务未开始或已暂停且数据已导出,然后再清空数据")return}err := model.ClearDatabase()if err != nil {dialog.ShowError(err, global.W)} else {global.InfoDialog("数据库清空成功!")ListTabelRefresh(1, 10)}global.ProcessBar.Hide()}}, global.W)})// 横向布局开始、导出、清空按钮btns := container.NewHBox(global.QueueBtn, export, clear)hed := container.NewBorder(nil, nil, label, btns, nil)// 列表布局list := Rowlist()global.ProcessBar = widget.NewProgressBar()process := model.CompletedCount()if global.Total > 0 {global.ProcessBar.SetValue(float64(process) / float64(global.Total))} else {global.ProcessBar.Hide()}barList := container.NewVBox(hed, global.ProcessBar)row := container.NewBorder(barList, nil, nil, nil, list)return row
}// 数据处理结果导出
func ExportData() {api := global.GetCurrentApi()cols := global.GetApiInfoSlice(api, "colums")cols = append([]string{"Id"}, cols...)// 打开文件保存地址fileDialog := dialog.NewFileSave(func(uc fyne.URIWriteCloser, err error) {if uc != nil {bar := widget.NewProgressBar()pc := container.NewGridWrap(fyne.NewSize(300, 20), bar)bs := dialog.NewCustom("导出进度", "数据写入中...", pc, global.W)bs.Show()f := xlsx.NewFile()sheet, err := f.AddSheet("Sheet3")if err != nil {global.LabelMsg.SetText(err.Error())}items, err := model.ExportDataList()if err != nil {global.LabelMsgText(err.Error())} else {for i, item := range items {bar.SetValue(float64(i+1) / float64(global.Total))if i == 0 {title := sheet.AddRow()for _, col := range cols {cell := title.AddCell()cell.Value = col}}row := sheet.AddRow()for _, col := range cols {cell := row.AddCell()var cellData stringswitch col {case "Id":cellData = fmt.Sprintf("%d", item.Id)case "姓名":cellData = item.Realnamecase "身份证号":cellData = item.Idcardcase "手机号":cellData = item.Mobilecase "银行卡号":cellData = item.Bankcardcase "结果":text := item.Resultif text == "1" {text = "一致"} else if text == "2" {text = "不一致"}cellData = textcase "描述":cellData = item.Msgcase "状态":text := "未处理"if item.Completed == 1 {text = "已处理"}cellData = textcase "任务Id":cellData = item.Jobiddefault:cellData = ""}cell.Value = cellData}}}err = f.Save(uc.URI().Path())if err != nil {global.LabelMsg.SetText(err.Error())}bs.SetDismissText("导出完成")uc.Close()time.Sleep(time.Second * 1)bs.Hide()}}, global.W)fileDialog.Resize(fyne.NewSize(1000, 600))fileDialog.SetFileName(api + "-export.xlsx")fileDialog.Show()}// 队列任务执行
func queueRun(btn *widget.Button) {api := global.GetCurrentApi()limit, _ := strconv.Atoi(global.GetPreferenceVal(api, "concurrency", "string", "1").(string))// 接口请求参数统一处理authKey := global.GetPreferenceVal(api, "key", "string", "").(string)// 接口标识,用于处理和提取接口参数var apiHeader, apiParams map[string]string// 接口地址url := global.GetApiInfoStr(api, "url")// 请求方式method := global.GetApiInfoStr(api, "method")// 请求headerapiHeaderTpl := global.GetApiInfoMap(api, "header")jmh, _ := json.Marshal(apiHeaderTpl)json.Unmarshal(jmh, &apiHeader)// 请求参数apiParamsTpl := global.GetApiInfoMap(api, "params")jmp, _ := json.Marshal(apiParamsTpl)json.Unmarshal(jmp, &apiParams)// 响应参数模板responseTpl := global.GetApiInfoMapI(api, "response")// 响应参数模板checkCode := global.GetApiInfoStr(api, "checkCode")fmt.Println("key:", authKey, apiParams)// 接口appkey参数传递方式authtype := global.GetApiInfoStr(api, "authtype")switch authtype {case "params":for k, v := range apiParams {if strings.Contains(v, "APP-KEY") {apiHeader[k] = strings.Replace(apiHeader[k], "APP-KEY", authKey, -1)}}case "header":for k, v := range apiHeader {if strings.Contains(v, "APP-KEY") {apiHeader[k] = strings.Replace(apiHeader[k], "APP-KEY", authKey, -1)}}}pcw := utils.NewPool(limit)process := 0refresh := truefor global.QueueStatus == "start" {rows, err := model.QueueList(limit)if err != nil {global.QueueStatus = "pause"global.QueueBtn.SetText("开始")global.QueueBtn.SetIcon(theme.MediaPlayIcon())global.LabelMsgText("当前任务已暂停")global.ErrorDialog("任务查询异常:" + err.Error())break}for _, item := range rows {pcw.Add(1)go apiRequest(item, pcw, url, method, checkCode, apiHeader, apiParams, responseTpl)}pcw.Wait()process = model.CompletedCount()global.ProcessBar.SetValue(float64(process) / float64(global.Total))fmt.Println("--执行任务:", limit, len(rows), process, global.Total, float64(process)/float64(global.Total), global.PageSize, process, refresh)if len(rows) < limit {global.QueueStatus = "end"}if global.PageSize < process && refresh {ListTabelRefresh(1, global.PageSize)refresh = false}}fmt.Println("任务暂停或结束:", global.QueueStatus)if global.QueueStatus == "end" {btn.SetIcon(theme.MediaStopIcon())btn.SetText("完成")ListTabelRefresh(1, global.PageSize)global.InfoDialog("任务执行结束,请及时导出数据")}if global.QueueStatus == "pause" {ListTabelRefresh(1, global.PageSize)}}// 接口请求
func apiRequest(row model.Persons, pcw *utils.PoolChanWt, url, method, checkCode string, apiHeader, apiParams map[string]string, responseTpl map[string]interface{}) {// 参数处理赋值for key, val := range apiParams {switch val {case "Realname":apiParams[key] = row.Realnamecase "Idcard":apiParams[key] = row.Idcardcase "Mobile":apiParams[key] = row.Mobilecase "Bankcard":apiParams[key] = row.Bankcard}}result := ApiRequstDispatch(url, method, checkCode, apiHeader, apiParams, responseTpl)model.UpdateResult(row, result)pcw.Done()}
6. 核验数据结果列表
导入文件的数据列表
核验结果列
package widgetsimport ("fmt""fyne_study/global""fyne_study/model""reflect""strconv""fyne.io/fyne/v2""fyne.io/fyne/v2/container""fyne.io/fyne/v2/data/binding""fyne.io/fyne/v2/widget""github.com/gogf/gf/v2/container/garray"
)// border : right
// func rowlist() *fyne.Container {
func Rowlist() *fyne.Container {// global.ListPerson, total = GetDataList(pageSize, currPage)fmt.Println("列表查询结果:", len(global.ListPerson), global.Total)api := global.GetCurrentApi()global.CurCols = global.GetApiInfoSlice(api, "colums")global.CurRxp = global.GetApiInfoStr(api, "codeExp")// 设置表头数据titles := []fyne.CanvasObject{}for i, item := range global.DefCols {lt := ""if i < len(global.CurCols) {lt = global.CurCols[i]}lbl := widget.NewLabel(lt)for _, w := range item {cl := container.NewGridWrap(fyne.NewSize(float32(w), 35), lbl)titles = append(titles, cl)}}global.ListTitle = container.NewHBox(titles...)// 列表信息global.ListTable = widget.NewList(func() int {return len(global.ListPerson)}, func() fyne.CanvasObject {titles := []fyne.CanvasObject{}for _, item := range global.DefCols {lbl := widget.NewLabel("")lbl.Wrapping = fyne.TextWrapWordfor _, w := range item {cl := container.NewGridWrap(fyne.NewSize(float32(w), 45), lbl)titles = append(titles, cl)}}vBox := container.NewHBox(titles...)return vBox// return widget.NewLabel("")}, func(index widget.ListItemID, co fyne.CanvasObject) {ListTabelItem(index, co)},)// 分页组装处理PaginatorCombing()return container.NewBorder(global.ListTitle, global.PaginatorBox, nil, nil, global.ListTable)
}// list刷新
func ListTabelRefresh(currPage, pageSize int) {var err errorglobal.ListPerson, global.Total, err = model.GetDataList(pageSize, currPage)if err != nil {global.LabelMsgText(err.Error())global.ErrorDialog(err.Error())return}fmt.Println("ListTabelRefresh:", currPage, pageSize, global.Total, len(global.ListPerson))global.Paginator = global.PaginatorFunc(currPage, pageSize, int64(global.Total))if len(global.PagesBox.Objects) > 0 {for r := global.Paginator.Totalpages; r < len(global.PagesBox.Objects) && r > 0; r++ {global.PagesBox.Remove(global.PagesBox.Objects[r])}}if global.PaginatorBox != nil {if global.Paginator.Totalpages == 0 {global.PaginatorBox.Hide()} else {global.PaginatorBox.Show()}}garray.NewIntArrayFrom(global.Paginator.Pages).Iterator(func(k, v int) bool {// 如果当前分页按钮数量小于5个,且k大于当前数量,则新增一个if len(global.PagesBox.Objects) < 5 && k+1 > len(global.PagesBox.Objects) {global.PagesBox.Add(widget.NewButton("1", nil))}if k > global.Paginator.Totalpages && global.Paginator.Totalpages < 5 {for r := global.Paginator.Totalpages; r < 5; r++ {global.PagesBox.Remove(global.PagesBox.Objects[r])}}btn := global.PagesBox.Objects[k].(*widget.Button)if v == currPage {btn.SetText(fmt.Sprintf("*%d", v))} else {btn.SetText(fmt.Sprintf("%d", v))}btn.OnTapped = func() {global.CurrPagedb.Set(v)}global.ListTable.Refresh()fmt.Println("--", v)return true})global.PaginatorLabel.SetText(fmt.Sprintf("%d/%d : %d", currPage, global.Paginator.Totalpages, global.Total))
}// 列表的每项
func ListTabelItem(index widget.ListItemID, co fyne.CanvasObject) {// VBox->GridWarp->LabeloneRow := co.(*fyne.Container)for i, col := range global.CurCols {label := oneRow.Objects[i].(*fyne.Container)if reflect.TypeOf(label.Objects[0]) == reflect.TypeOf(widget.NewLabel("")) {lb1 := label.Objects[0].(*widget.Label)switch col {case "姓名":s := fmt.Sprintf("%d = %s", global.ListPerson[index].Id, global.ListPerson[index].Realname)lb1.SetText(s)case "身份证号":lb1.SetText(global.ListPerson[index].Idcard)case "手机号":lb1.SetText(global.ListPerson[index].Mobile)case "银行卡号":lb1.SetText(global.ListPerson[index].Bankcard)case "结果":text := global.ListPerson[index].Resultif text == "1" {text = "一致"} else if text == "2" {text = "不一致"}lb1.SetText(text)case "描述":lb1.SetText(global.ListPerson[index].Msg)case "状态":text := "未处理"if global.ListPerson[index].Completed == 1 {text = "已处理"}lb1.SetText(text)case "任务Id":lb1.SetText(global.ListPerson[index].Jobid)default:lb1.SetText("")}} else {fmt.Println("===", reflect.TypeOf(label.Objects[0]))}}
}// 分页数据项组装
func PaginatorCombing() {currPage := 1// 添加分页按钮,绑定当前值,并监听页面变更global.CurrPagedb = binding.BindInt(&currPage)global.PageSized = binding.BindInt(&global.PageSize)global.CurrPagedb.AddListener(binding.NewDataListener(func() {ListTabelRefresh(currPage, global.PageSize)}))// 处理分页global.Paginator = global.PaginatorFunc(currPage, global.PageSize, int64(global.Total))// 默认显示5页按钮global.PagesBox = container.NewHBox()box := 5if box > global.Paginator.Totalpages {box = global.Paginator.Totalpages}for i := 0; i < box; i++ {global.PagesBox.Add(widget.NewButton("1", nil))}global.PaginatorLabel = widget.NewLabel(fmt.Sprintf("%d/%d : %d", currPage, global.Paginator.Totalpages, global.Total))first := widget.NewButton("第一页", func() {global.CurrPagedb.Set(1)})pre := widget.NewButton("上一页", func() {global.CurrPagedb.Set(global.Paginator.Perpage)})next := widget.NewButton("下一页", func() {global.CurrPagedb.Set(global.Paginator.Nextpage)})last := widget.NewButton("尾页", nil)last.OnTapped = func() {global.CurrPagedb.Set(global.Paginator.Totalpages)}// 每页数据选择pageSizeSel := widget.NewSelect([]string{"10", "15", "20", "50", "100"}, nil)pageSizeSel.OnChanged = func(pg string) {// nums = 0ps, _ := strconv.Atoi(pg)global.PageSized.Set(ps)ListTabelRefresh(currPage, ps)}pageSizeSel.SetSelected(strconv.Itoa(global.PageSize))global.PaginatorBox = container.NewHBox(first, pre, global.PagesBox, next, last, global.PaginatorLabel, pageSizeSel)if global.Paginator.Totalpages > 0 {global.PaginatorBox.Show()} else {global.PaginatorBox.Hide()}
}
注意:
- 1、应用第一次启动时候会在当前目录下初始化数据库文件
- 2、在切换接口前确保之前的数据已导出
- 3、请严格按照模板格式上传数据
- 4、一定要少量数据先测试、功能无误再大量测试,避免不必要的损失
- 5、该工具仅供交流学习使用,因此产生的任何损失概不负责
应用下载:本地批量核验工具
福利彩蛋:没有好玩的 API 接口?上百款免费接口等你来,免费 API,免费 API 大全