使用asp.net mvc,boostrap及knockout.js开发微信自定义菜单编辑工具(推荐)
前言
微信的接口调试工具可以编辑自定义菜单,不过是提交json格式数据创建菜单,非常的不方便还容易出错。网上的工具不好用,所以就自己写了一个。
正文
先用bootstrap排个页面框架出来,调用自定义菜单接口需要用到AccessToken,放个输入框输入AccessToken。也不排除想直接输入AppId和AppSecret来获取AccessToken的用户,所以还需要下拉菜单来选择是输入AccessToken还是直接获取AccessToken。为了兼顾微信企业号应用创建菜单还需要AgentId,CorpId,套件永久授权码,SuiteId,SuiteSecret,SuiteTicket,参数的输入框大致就是这些。
使用knockout定义好observables监控属性。并绑定到输入框上。
定义菜单展示及菜单编辑模块,排版为微信公众号菜单三个大菜单,每个大菜单下面可以配五个子菜单。大致思路如下,页面排版为六行三列,三个大菜单未配置满时在右侧显示增加菜单按钮,
每个父级菜单的子菜单未配置满时在上方显示增加菜单按钮。未配置满时以空白div占位。
定义个函数生成自定义长度数组
使用knockout定义好菜单监控属性,格式为
{ "button": [ { "name": "父级菜单1", "sub_button": [ { "type": "view", "name": "子菜单1", "url": "" } ] }, { "name": "父级菜单1", "sub_button": [ { "type": "view", "name": "子菜单2", "url": "" }, { "type": "view", "name": "子菜单1", "url": "" } ] } ] }
定义添加,编辑,删除菜单函数,定义添加编辑菜单时临时监控属性,定义当前编辑菜单索引的监控属性。
一个一个编辑菜单还不是很方便,所以还要定义菜单的 上 下 左 右 的移动,及复制粘贴功能。
function MenuFormValidate() { $("#MenuForm").validate({ rules: { name: { required: true }, value: { required: false } }, messages: { name: { required: "请输入名称" }, value: { required: $("#txtMenuButtonValue").attr("placeholder") } } }); } MenusReset:function () { var menus = JSON.stringify(model.Menus()); model.Menus(undefined); model.Menus(JSON.parse(menus));//刷新菜单对象 MenuFormValidate();//重新绑定验证方法 }, MenuIndex: ko.observable(), //父级菜单索引 isEditMenu: ko.observable(false), //是否是编辑菜单 BottonIndex: ko.observable(-1), //编辑菜单的父级菜单索引 SubBottonIndex: ko.observable(-1), //编辑菜单的子菜单索引 Menu: ko.observable(),//编辑菜单时临时监控属性 CopyMenu: ko.observable(),//复制的菜单对象 Copy: function () { //复制 if (model.Menu() != undefined) { var menu = JSON.stringify(model.Menu()); model.CopyMenu(JSON.parse(menu)); model.Menu(undefined); } }, Paste: function () {//粘贴 if (model.CopyMenu() != undefined) { var menu = JSON.parse(JSON.stringify(model.CopyMenu())); if (model.SubBottonIndex() !== -1 && menu.sub_button != undefined || (!model.isEditMenu() && model.MenuIndex() != undefined)) { delete menu.sub_button; } model.Menu(menu); MenuFormValidate(); } }, Up: function () {//向上移动 var bottonIndex = model.BottonIndex(); var subBottonIndex = model.SubBottonIndex(); var newSubBottonIndex = subBottonIndex - 1; model.Menus().button[bottonIndex].sub_button[subBottonIndex] = model.Menus().button[bottonIndex].sub_button[newSubBottonIndex]; model.Menus().button[bottonIndex].sub_button[newSubBottonIndex] = model.Menu(); model.MenusReset(); model.SubBottonIndex(newSubBottonIndex); }, Down: function () {//向下移动 var bottonIndex = model.BottonIndex(); var subBottonIndex = model.SubBottonIndex(); var newSubBottonIndex = subBottonIndex + 1; model.Menus().button[bottonIndex].sub_button[subBottonIndex] = model.Menus().button[bottonIndex].sub_button[newSubBottonIndex]; model.Menus().button[bottonIndex].sub_button[newSubBottonIndex] = model.Menu(); model.MenusReset(); model.SubBottonIndex(newSubBottonIndex); }, Left: function () {//向左移动 var bottonIndex = model.BottonIndex(); var subBottonIndex = model.SubBottonIndex(); if (subBottonIndex === -1) { var newBottonIndex = bottonIndex - 1; model.Menus().button[bottonIndex] = model.Menus().button[newBottonIndex]; model.Menus().button[newBottonIndex] = model.Menu(); model.MenusReset(); model.BottonIndex(newBottonIndex); } }, Right: function () {//向右移动 var bottonIndex = model.BottonIndex(); var subBottonIndex = model.SubBottonIndex(); if (subBottonIndex === -1) { var newBottonIndex = bottonIndex + 1; model.Menus().button[bottonIndex] = model.Menus().button[newBottonIndex]; model.Menus().button[newBottonIndex] = model.Menu(); model.MenusReset(); model.BottonIndex(newBottonIndex); } }, EditMenu: function (obj, bottonindex, subbottonindex) {//编辑菜单 model.BottonIndex(bottonindex); model.SubBottonIndex(subbottonindex); model.isEditMenu(true); var data = JSON.stringify(obj); model.Menu(JSON.parse(data)); MenuFormValidate(); }, AddMenu: function (index) {//添加菜单 model.BottonIndex(-1); model.SubBottonIndex(-1); model.isEditMenu(false); model.MenuIndex(index); var menu = { type: "view", name: "", value: "" }; model.Menu(menu); MenuFormValidate(); }, DeleteMenu: function () {//删除菜单 $(model.Menus().button).each(function (index, item) { if (index === model.BottonIndex() && model.SubBottonIndex() === -1) { model.Menus().button.splice(index, 1); } if (item.sub_button instanceof Array) { $(item.sub_button).each(function (index1) { if (index === model.BottonIndex() && index1 === model.SubBottonIndex()) { item.sub_button.splice(index1, 1); } }); } }); model.Menu(undefined); model.MenuIndex(undefined); model.BottonIndex(-1); model.SubBottonIndex(-1); model.MenusReset(); }, CancelMenuSave: function () {//取消编辑,重置参数 model.Menu(undefined); model.MenuIndex(undefined); model.BottonIndex(-1); model.SubBottonIndex(-1); }, MenuSave: function () {//保存编辑的菜单 if (!$("#MenuForm").data("validator").form()) { return; } if (model.isEditMenu()) { var menuIndex = model.BottonIndex(); var subMenuIndex = model.SubBottonIndex(); if (subMenuIndex === -1) { model.Menus().button[menuIndex] = model.Menu(); } else { model.Menus().button[menuIndex].sub_button[subMenuIndex] = model.Menu(); } } else { if (model.MenuIndex() != undefined) { if (model.Menus().button[model.MenuIndex()].sub_button == undefined) { model.Menus().button[model.MenuIndex()].sub_button = new Array(); } model.Menus().button[model.MenuIndex()].sub_button.unshift(model.Menu()); } else { model.Menus().button.push(model.Menu()); } } model.Menu(undefined); model.MenuIndex(undefined); model.BottonIndex(-1); model.SubBottonIndex(-1); model.MenusReset(); },
绑定好监控属性,生成菜单排版
<div class="panel-body" data-bind="with:Menus" id="divMenu" style="display: none;"> <div style="height: 200px;" data-bind="foreach:newArray(3)"> <div class="list-group col-xs-4 clearFill bn"> <!--ko if:($parent.button.length>0 && $parent.button[$index()]!=undefined && $parent.button[$index()].sub_button!=undefined ) --> <!--ko foreach:newArray((4-$parent.button[$index()].sub_button.length)) --> <div class="list-group-item bn"></div> <!--/ko--> <!--ko if:$parent.button[$index()].sub_button.length<5 --> <div class="list-group-item" data-bind="click:function (){$root.AddMenu($index())}"><i class="fa fa-plus"></i> </div> <!--/ko--> <!--ko foreach:($parent.button[$index()].sub_button) --> <div class="list-group-item" data-bind="text:name,attr:{'bottonIndex':$parent.value,'subbottonIndex':$index()},click:function (){$root.EditMenu($data,$parent.value,$index())}"></div> <!--/ko--> <!--/ko --> <!--ko if: $parent.button[$index()]!=undefined && $parent.button[$index()].sub_button==undefined --> <div class="list-group-item bn"></div> <div class="list-group-item bn"></div> <div class="list-group-item bn"></div> <div class="list-group-item bn"></div> <div class="list-group-item" data-bind="click:function (){$root.AddMenu($index())}"><i class="fa fa-plus"></i> </div> <!--/ko--> <!--ko if: $parent.button[$index()]==undefined --> <div class="list-group-item bn"></div> <div class="list-group-item bn"></div> <div class="list-group-item bn"></div> <div class="list-group-item bn"></div> <div class="list-group-item bn"></div> <!--/ko--> </div> </div> <!--ko foreach:button --> <div class="col-xs-4 list-group-item list-group-item-danger" data-bind="text:name,attr:{'bottonindex':$index()},click:function (){$root.EditMenu($data,$index(),-1)}"></div> <!--/ko--> <!--ko if:button.length < 3 --> <div class="col-xs-4 list-group-item" data-bind="click:function (){$root.AddMenu();}"><i class="fa fa-plus"></i> </div> <!--/ko--> <div class="clearfix"></div> <div class="col-xs-12" style="border: 1px solid #EEEEEE; padding-top: 15px; margin-top: 15px;" data-bind="with:$root.Menu,visible:($root.Menu()!=undefined)"> <form id="MenuForm" onsubmit="return false;"> <div class="form-group col-xs-4"> <input type="text" class="form-control" name="name" placeholder="请输入名称" data-bind="value:name"> </div> <div class="form-group col-xs-4"> <select class="form-control" onchange="$('#txtMenuButtonValue') .attr('placeholder', $(this).find('option:selected').attr('pl'))" data-bind="value:type"> <option value="view" pl="请输入Url">跳转URL</option> <option value="click" pl="请输入Key">点击推事件</option> <option value="scancode_push" pl="请输入Key">扫码推事件</option> <option value="scancode_waitmsg" pl="请输入Key">扫码推事件且弹出“消息接收中”提示框</option> <option value="pic_sysphoto" pl="请输入Key">弹出系统拍照发图</option> <option value="pic_photo_or_album" pl="请输入Key">弹出拍照或者相册发图</option> <option value="pic_weixin" pl="请输入Key"> 弹出微信相册发图器</option> <option value="location_select" pl="请输入Key">弹出地理位置选择器</option> </select> </div> <div class="form-group col-xs-8"> <input type="text" id="txtMenuButtonValue" name="value" class="form-control" placeholder="请输入Url" data-bind="value:value"> </div> <div class="form-group col-xs-12"> <button type="submit" class="btn btn-primary" data-bind="click:$root.MenuSave">确定</button> <button type="submit" class="btn btn-danger" data-bind="visible:$root.isEditMenu,click:$root.DeleteMenu">删除</button> <button type="button" class="btn btn-default" title="上移" data-bind="visible:$root.isEditMenu(),disable:!$root.IsUp(),click:$root.Up"><i class="fa fa-chevron-circle-up" aria-hidden="true"></i></button> <button type="button" class="btn btn-default" title="下移" data-bind="visible:$root.isEditMenu(),disable:!$root.IsDown(),click:$root.Down"><i class="fa fa-chevron-circle-down" aria-hidden="true"></i></button> <button type="button" class="btn btn-default" title="左移" data-bind="visible:$root.isEditMenu(),disable:!$root.IsLeft(),click:$root.Left"><i class="fa fa-chevron-circle-left" aria-hidden="true"></i></button> <button type="button" class="btn btn-default" title="右移" data-bind="visible:$root.isEditMenu(),disable:!$root.IsRight(),click:$root.Right"><i class="fa fa-chevron-circle-right" aria-hidden="true"></i></button> <button type="button" class="btn btn-default" title="复制菜单" data-bind="visible:$root.isEditMenu(),click:$root.Copy">复制</button> <button type="button" class="btn btn-default" title="粘贴菜单" data-bind="click:$root.Paste">粘贴</button> <button type="submit" class="btn btn-default" data-bind="click:$root.CancelMenuSave">关闭</button> </div> </form> </div> <div class="clearfix"></div> </div>
最后增加菜单的查询函数及发布函数。因为编辑菜单方便,菜单对象和微信自定义菜单接口所需要的json格式不对应,所以在查询现有菜单和发布菜单时,需要对json数据进行一下格式变化。,
EditMenus: function (isQuery) { if (isQuery == undefined) { var menu = {}; menu.button = new Array(); model.Menus(menu); } else { var appId = model.AppId(); var appSecret = model.AppSecret(); var accessToken = model.AccessToken(); var type = model.Type(); var tokenType = model.TokenType(); var corpId = model.CorpId(); var permanentCode = model.PermanentCode(); var agentId = model.AgentId(); var suiteId = model.SuiteId(); var suiteSecret = model.SuiteSecret(); var suiteTicket = model.SuiteTicket(); if (type === "1" && tokenType === "2") { if (appId == undefined || $.trim(appId).length === 0) { alert("请输入AppId"); return; } if (appSecret == undefined || $.trim(appSecret).length === 0) { alert("请输入AppSecret"); return; } } else if (type === "2" && tokenType === "2") { if (corpId == undefined || $.trim(corpId).length === 0) { alert("请输入CorpId"); return; } if (permanentCode == undefined || $.trim(permanentCode).length === 0) { alert("请输入永久授权码"); return; } if (agentId == undefined || $.trim(agentId).length === 0) { alert("请输入AgentId"); return; } if (suiteId == undefined || $.trim(suiteId).length === 0) { alert("请输入SuiteId"); return; } if (suiteSecret == undefined || $.trim(suiteSecret).length === 0) { alert("请输入SuiteSecret"); return; } if (suiteTicket == undefined || $.trim(suiteTicket).length === 0) { alert("请输入SuiteTicket"); return; } } else if (tokenType === "1") { if (accessToken == undefined || $.trim(accessToken).length === 0) { alert("请输入AccessToken"); return; } } $("#btnQueryMenu").button("查询中..."); $.ajax({ url: "", datatype: "JSON", type: "POST", async: true, data: JSON.stringify({ appId: appId, appSecret: appSecret, accessToken: accessToken, type: type, tokenType: tokenType, corpId: corpId, permanentCode: permanentCode, agentId: agentId, suiteId: suiteId, suiteSecret: suiteSecret, suiteTicket: suiteTicket }), contentType: "application/json; charset=UTF-8", success: function (obj) { $("#btnQueryMenu").button("reset"); if (obj.Success) { var data = obj.Data; var menus = JSON.parse(data).menu; $(menus.button).each(function (index, item) { if (item.type === "view") { item.value = item.url; delete item.url; } else { item.value = item.key; delete item.key; } if (item.type == undefined) { item.type = "view"; item.value = ""; } if (item.sub_button instanceof Array) { $(item.sub_button).each(function (index1, item2) { if (item2.type === "view") { item2.value = item2.url; delete item2.url; } else { item2.value = item2.key; delete item2.key; } }); } }); model.Menu(undefined); model.MenuIndex(undefined); model.BottonIndex(-1); model.SubBottonIndex(-1); model.Menus(undefined); model.Menus(menus); } else { alert(obj.Messages); } }, error: function (xmlHttpRequest, textStatus, errorThrown) { $("#btnQueryMenu").button("reset"); console.error(errorThrown); } }); } }, SaveMenus: function () { var menus = JSON.parse(JSON.stringify(model.Menus())); $(menus.button).each(function (index, item) { if (item.type === "view") { item.url = item.value; delete item.value; } else { item.key = item.value; delete item.value; } if (item.sub_button instanceof Array) { $(item.sub_button).each(function (index1, item2) { if (item2.type === "view") { item2.url = item2.value; delete item2.value; } else { item2.key = item2.value; delete item2.value; } }); if (item.sub_button.length > 0) { delete item.key; delete item.url; delete item.type; } else { delete item.sub_button; } } }); console.log(JSON.stringify(menus)); var appId = model.AppId(); var appSecret = model.AppSecret(); var accessToken = model.AccessToken(); var type = model.Type(); var tokenType = model.TokenType(); var agentId = model.AgentId(); var suiteId = model.SuiteId(); var suiteSecret = model.SuiteSecret(); var suiteTicket = model.SuiteTicket(); if (type === "1" && tokenType === "2") { if (appId == undefined || $.trim(appId).length === 0) { alert("请输入AppId"); return; } if (appSecret == undefined || $.trim(appSecret).length === 0) { alert("请输入AppSecret"); return; } } else if (type === "2" && tokenType === "2") { if (agentId == undefined || $.trim(agentId).length === 0) { alert("请输入AgentId"); return; } if (suiteId == undefined || $.trim(suiteId).length === 0) { alert("请输入SuiteId"); return; } if (suiteSecret == undefined || $.trim(suiteSecret).length === 0) { alert("请输入SuiteSecret"); return; } if (suiteTicket == undefined || $.trim(suiteTicket).length === 0) { alert("请输入SuiteTicket"); return; } } else if (tokenType === "1") { if (accessToken == undefined || $.trim(accessToken).length === 0) { alert("请输入AccessToken"); return; } } $("#btnSubmitMenu").button("发布中..."); $.ajax({ url: "", datatype: "JSON", type: "POST", async: true, data: JSON.stringify({ appId: appId, appSecret: appSecret, accessToken: accessToken, type: type, tokenType: tokenType, agentId: agentId, suiteId: suiteId, suiteSecret: suiteSecret, suiteTicket: suiteTicket, menu: JSON.stringify(menus) }), contentType: "application/json; charset=UTF-8", success: function (obj) { $("#btnSubmitMenu").button("reset"); if (obj.Success) { alert("发布成功"); } else { alert(obj.Messages); } }, error: function (xmlHttpRequest, textStatus, errorThrown) { $("#btnSubmitMenu").button("reset"); console.error(errorThrown); } }); }
最终效果如下
以上所述是小编给大家介绍的使用asp.net mvc,boostrap及knockout.js开发微信自定义菜单编辑工具,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对猪先飞网站的支持!
相关文章
- 这篇文章主要为大家详细介绍了ASP.NET购物车的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-22
- 这篇文章主要介绍了Bootstrap Table使用整理(二)的相关资料,需要的朋友可以参考下...2017-06-15
- 在开发过程中,使用Visual Studio的断点调试功能可以很方便帮我们调试发现程序存在的错误,同样Visual Studio也支持对SQL Server里面的存储过程进行调试,下面就让我们看看具体的调试方法。...2021-09-22
基于BootStrap Metronic开发框架经验小结【八】框架功能总体界面介绍
这篇文章主要介绍了基于BootStrap Metronic开发框架经验小结【八】框架功能总体界面介绍 的相关资料,需要的朋友可以参考下...2016-05-14ASP.NET Core根据环境变量支持多个 appsettings.json配置文件
这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22- 这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
- 这篇文章主要教大家如何利用Bootstrap3制作图片轮播效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2016-05-14
- 这篇文章主要为大家分享了Bootstrap教程JS插件滚动监听学习笔记,内容很详细,感兴趣的小伙伴们可以参考一下...2016-05-20
- 这篇文章主要介绍了SpringMVC文件上传原理及实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-15
- 在网页中,经常见到进度条效果,那么这些个性的进度条组件效果是怎么实现的呢,下面脚本之家小编给大家分享Bootstrap进度条组件知识详解,感兴趣的朋友要求学习吧...2016-05-04
- 这篇文章主要为大家详细解析了BootStrap图片样式、辅助类样式和CSS组件源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-01-23
C# MVC模式中应该怎样区分应用程序逻辑(Controller层)和业务逻辑(Model层)?
这篇文章主要介绍了C# MVC模式中应该怎样区分应用程序逻辑(Controller层)和业务逻辑(Model层)?,这也小编做.NET项目时经常思考和让人混乱的一个问题,这篇文章写的挺好,一下清晰了许多,需要的朋友可以参考下...2020-06-25JS树形菜单组件Bootstrap TreeView使用方法详解
这篇文章主要为大家详细介绍了js组件Bootstrap TreeView使用方法,本文一部分针对于bootstrap的treeview的实践,另一部分是介绍自己写的树形菜单,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-01-09- 这篇文章主要为大家详细解析了BootStrap栅格系统、表单样式与按钮样式源码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-01-23
- 这篇文章主要为大家详细介绍了Bootstrap树形控件使用方法,感兴趣的小伙伴们可以参考一下...2016-01-29
详解ASP.NET Core 中基于工厂的中间件激活的实现方法
这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22- 这篇文章主要为大家详细介绍了Bootstrap选项卡学习笔记,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2017-02-19
基于BootStrap的Metronic框架实现页面链接收藏夹功能按钮移动收藏记录(使用Sortable进行拖动排序)
这篇文章主要介绍了基于BootStrap的Metronic框架实现页面链接收藏夹功能按钮移动收藏记录(使用Sortable进行拖动排序)的相关资料,非常不错,需要的朋友可以参考下...2016-09-01基于BootStrap Metronic开发框架经验小结【六】对话框及提示框的处理和优化
这篇文章主要介绍了基于BootStrap Metronic开发框架经验小结【六】对话框及提示框的处理和优化的相关知识,主要对比说明在Bootstrap开发中用到的这些技术要点,对此文感兴趣的朋友一起学习吧...2016-05-14基于Bootstrap的Metronic框架实现页面链接收藏夹功能
本文给大家介绍基于Metronic的Bootstrap开发框架实现页面链接收藏夹功能,非常不错,具有参考借鉴价值,感兴趣的朋友一起学习吧...2016-09-01