golang跨平台GUI框架fyne介绍与使用详解,开放案例

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 大全

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/36461.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CVPR 2024 | 双手协作双物体的数据集TACO:引领可泛化手物交互的新方向

论文题目&#xff1a; TACO: Benchmarking Generalizable Bimanual Tool-ACtion-Object Understanding 论文链接&#xff1a; https://arxiv.org/pdf/2401.08399.pdf 项目主页&#xff1a; https://taco2024.github.io/ 视频链接&#xff1a; https://www.youtube.com/watch…

完全离线的本地问答模型LocalGPT如何实现无公网IP远程连接提问

文章目录 前言环境准备1. localGPT部署2. 启动和使用3. 安装cpolar 内网穿透4. 创建公网地址5. 公网地址访问6. 固定公网地址 前言 本文主要介绍如何本地部署LocalGPT并实现远程访问&#xff0c;由于localGPT只能通过本地局域网IP地址端口号的形式访问&#xff0c;实现远程访问…

技术驱动的音乐变革:AI带来的产业重塑

&#x1f4d1;引言 近一个月来&#xff0c;随着几款音乐大模型的轮番上线&#xff0c;AI在音乐产业的角色迅速扩大。这些模型不仅将音乐创作的门槛降至前所未有的低点&#xff0c;还引发了一场关于AI是否会彻底颠覆音乐行业的激烈讨论。从初期的兴奋到现在的理性审视&#xff0…

石家庄高校大学智能制造实验室数字孪生可视化系统平台项目验收

智能制造作为未来制造业的发展方向&#xff0c;已成为各国竞相发展的重点领域。石家庄高校大学智能制造实验室积极响应国家发展战略&#xff0c;结合自身优势&#xff0c;决定引进数字孪生技术&#xff0c;构建一个集教学、科研、生产于一体的可视化系统平台。 数字孪生可视化…

免费内网穿透、配置超级简单

巴比达内网穿透 曾经那些所谓的内网穿透服务&#xff0c;给我带来的只有无尽的烦恼。有的像&#xff0c;毫无规律地每天更改固定访问地址和端口。有一次&#xff0c;我正在进行一个重要的项目投标&#xff0c;需要及时与团队成员共享文件和沟通。可就在关键时刻&#xff0c;网络…

endswith()方法——是否以指定子字符串结尾

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 endswith()方法用于检索字符串是否以指定子字符串结尾。如果是则返回True&#xff0c;否则返回False。endswith()方法的语法格式如下&…

启智畅想:AI集装箱箱号识别系统,解决方案提供商

AI集装箱箱号识别系统 当前,智能卡口管理行业正处于快速发展的阶段。随着物联网、大数据、人工智能等技术的不断进步,智能卡口管理系统已经能够实现对集装箱运输的全程跟踪、监控和管理,大大提高了管理效率和安全性。然而,市场上现有的智能卡口管理系统仍然存在一些痛点问题,如…

JAVA-矩阵置零

给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 思路&#xff1a; 找到0的位置&#xff0c;把0出现的数组的其他值夜置为0 需要额外空间方法&#xff1a; 1、定义两个布尔数组标记二维数组中行和列…

BUUCTF--WEB

首頁 - OWASP Top 10:2021 [极客大挑战 2019]EasySQL 类型:sql注入 使用万能密码 flag{f580db5b-c0c9-4b13-bfb6-adfa525c93f5} [极客大挑战 2019]Havefun 类型:代码审计 F12打开浏览器控制台 GET请求,在url添加参数/?cat=dog访问 返回flag{f60c7d5c-9f44-4e92-88c0…

Springboot下使用Redis管道(pipeline)进行批量操作

之前有业务场景需要批量插入数据到Redis中&#xff0c;做的过程中也有一些感悟&#xff0c;因此记录下来&#xff0c;以防忘记。下面的内容会涉及到 分别使用for、管道处理批量操作&#xff0c;比较其所花费时间。 分别使用RedisCallback、SessionCallback进行Redis pipeline …

Mathematica训练课(44)-- 一些符号#,,//, /. 的整理

①“//”在后面写成你要执行的操作,即可执行。 注意:这一函数作用域标志的优先级是很靠后的,也就是说它会对一整行式子作用。 ②@的作用是在@后面的第一个元素进行操作 Sqrt @ a(*@作用在@后面、对离@最近的仅仅一个元素作用*) 例如,下面 若作用对象外面套着{},那么就要…

网络研究观:网络犯罪简报

通过犯罪研究人员精选的新闻提要了解最新的全球网络犯罪威胁。 了解不同的数字欺诈以及如何保护自己。 1&#xff1a;NIA 指控五人涉嫌国际人口贩卖和网络诈骗 印度国家调查局指控五名嫌疑人涉嫌一起重大人口贩卖和网络诈骗案&#xff0c;该案涉及印度青年被迫进入老挝的诈骗…

Python中常用的有7种值(数据)的类型及type()语句的用法

目录 0.Python中常用的有7种值&#xff08;数据&#xff09;的类型Python中的数据类型主要有&#xff1a;Number&#xff08;数字&#xff09;、Boolean&#xff08;布尔&#xff09;、String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、Tuple&#xf…

使用java代码实现GUI画面的简易项目操作

要使用Java创建一个图形用户界面&#xff08;GUI&#xff09;&#xff0c;我们可以使用Swing库&#xff0c;它是Java提供的一个标准GUI工具包。以下是一个简单的Java Swing程序示例&#xff0c;它创建了一个窗口&#xff08;JFrame&#xff09;&#xff0c;并在其中添加了一个标…

我重生了,学会了珂朵莉树

还玩线段树吗&#xff1f; 前言&注明 我好像一万年没更新了&#xff1f; 化学&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff…

检索增强生成 (RAG):揭开这一术语的神秘面纱并解释其带来的价值

一、介绍 如今&#xff0c;数据已成为新的黄金&#xff0c;而高效筛选这些丰富信息的能力则是成功企业脱颖而出的关键。Retrieval Augmented Generation&#xff08;RAG&#xff09;是创新的标杆&#xff0c;尤其是在知识管理领域。它不再只是为了存储信息&#xff0c;而是为了…

在运行中遇到扫描包问题

问题描述&#xff1a;当我们看到这个上面一行代码时就代表我们有个包没有被当前的Spring容器给扫描到&#xff0c;关于这个问题我们有两个&#xff1a;第一把整个包导进来&#xff0c;第二用哪个导哪个

Linux Swap

Swap: 页面换出&#xff1a;就是在 Swap 机制下&#xff0c;当内存资源紧张时&#xff0c;内核就会把不经常使用的这些匿名页中的数据写入到 Swap 分区或者 Swap 文件中。从而释放这些数据所占用的内存空间。 页面换入&#xff1a;就是当进程再次访问那些被换出的数据时&…

7 个不容忽视的开源安全工具

专业人士选择的第一个工具通常是开源选项,因为它们得到了广泛社区的保证和支持。此代码是支持安全可靠的互联网的基础的一部分。 最近,XZ Utils 等丑闻让用户犹豫不决。开放性是否是攻击的危险载体?还有其他问题在等着他们吗? 辩护者指出,虽然开放性可以让某些攻击变得更…

深度学习实战82-新的研究方向:大模型与图模型结合生成大型图模型,大图模型相关挑战和机遇的观点

大家好,我是微学AI,今天给大家介绍一下深度学习实战82-新的研究方向:大模型与图模型结合生成大型图模型,大图模型相关挑战和机遇的观点。随着人工智能的飞速发展,大型模型已成为人工智能领域最新的突破性成就。在图方面,大型模型尚未取得与自然语言处理和计算机视觉等其他…