首页 > 代码库 > 使用asp.net mvc,boostrap及knockout.js开发微信自定义菜单编辑工具

使用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": ""
                }
            ]
        }
    ]
}

技术分享

   定义添加,编辑,删除菜单函数,定义添加编辑菜单时临时监控属性,定义当前编辑菜单索引的监控属性。

  一个一个编辑菜单还不是很方便,所以还要定义菜单的 上 下 左 右 的移动,及复制粘贴功能。

  

 1   function MenuFormValidate() {
 2             $("#MenuForm").validate({
 3                 rules: {
 4                     name: {
 5                         required: true
 6                     },
 7                     value: {
 8                         required: false
 9                     }
10                 },
11                 messages: {
12                     name: {
13                         required: "请输入名称"
14                     },
15                     value: {
16                         required: $("#txtMenuButtonValue").attr("placeholder")
17                     }
18                 }
19             });
20         }
21 

22          MenusReset:function () { 23 var menus = JSON.stringify(model.Menus()); 24 model.Menus(undefined); 25 model.Menus(JSON.parse(menus));//刷新菜单对象 26 MenuFormValidate();//重新绑定验证方法 27 },

 

 

 

  1                 MenuIndex: ko.observable(), //父级菜单索引
  2                 isEditMenu: ko.observable(false), //是否是编辑菜单
  3                 BottonIndex: ko.observable(-1),  //编辑菜单的父级菜单索引
  4                 SubBottonIndex: ko.observable(-1), //编辑菜单的子菜单索引
  5                 Menu: ko.observable(),//编辑菜单时临时监控属性
  6                 CopyMenu: ko.observable(),//复制的菜单对象
  7                 Copy: function () { //复制
  8                     if (model.Menu() != undefined) {
  9                         var menu = JSON.stringify(model.Menu());
 10                         model.CopyMenu(JSON.parse(menu));
 11                         model.Menu(undefined);
 12                     }
 13                 },
 14                 Paste: function () {//粘贴
 15                     if (model.CopyMenu() != undefined) {
 16                         var menu = JSON.parse(JSON.stringify(model.CopyMenu()));
 17                         if (model.SubBottonIndex() !== -1 && menu.sub_button != undefined || (!model.isEditMenu() && model.MenuIndex() != undefined)) {
 18                             delete menu.sub_button;
 19                         }
 20 
 21                         model.Menu(menu);
 22                         MenuFormValidate();
 23                     }
 24                 },
 25                 Up: function () {//向上移动
 26                     var bottonIndex = model.BottonIndex();
 27                     var subBottonIndex = model.SubBottonIndex();
 28                     var newSubBottonIndex = subBottonIndex - 1;
 29                     model.Menus().button[bottonIndex].sub_button[subBottonIndex] = model.Menus().button[bottonIndex].sub_button[newSubBottonIndex];
 30                     model.Menus().button[bottonIndex].sub_button[newSubBottonIndex] = model.Menu();
 31                     model.MenusReset();
 32                     model.SubBottonIndex(newSubBottonIndex);
 33                 },
 34                 Down: function () {//向下移动
 35                     var bottonIndex = model.BottonIndex();
 36                     var subBottonIndex = model.SubBottonIndex();
 37                     var newSubBottonIndex = subBottonIndex + 1;
 38                     model.Menus().button[bottonIndex].sub_button[subBottonIndex] = model.Menus().button[bottonIndex].sub_button[newSubBottonIndex];
 39                     model.Menus().button[bottonIndex].sub_button[newSubBottonIndex] = model.Menu();
 40                     model.MenusReset();
 41                     model.SubBottonIndex(newSubBottonIndex);
 42                 },
 43                 Left: function () {//向左移动
 44                     var bottonIndex = model.BottonIndex();
 45                     var subBottonIndex = model.SubBottonIndex();
 46 
 47                     if (subBottonIndex === -1) {
 48                         var newBottonIndex = bottonIndex - 1;
 49                         model.Menus().button[bottonIndex] = model.Menus().button[newBottonIndex];
 50                         model.Menus().button[newBottonIndex] = model.Menu();
 51                         model.MenusReset();
 52                         model.BottonIndex(newBottonIndex);
 53                     }
 54                 },
 55                 Right: function () {//向右移动
 56                     var bottonIndex = model.BottonIndex();
 57                     var subBottonIndex = model.SubBottonIndex();
 58 
 59                     if (subBottonIndex === -1) {
 60                         var newBottonIndex = bottonIndex + 1;
 61                         model.Menus().button[bottonIndex] = model.Menus().button[newBottonIndex];
 62                         model.Menus().button[newBottonIndex] = model.Menu();
 63                         model.MenusReset();
 64                         model.BottonIndex(newBottonIndex);
 65                     }
 66                 },
 67                 EditMenu: function (obj, bottonindex, subbottonindex) {//编辑菜单
 68                     model.BottonIndex(bottonindex);
 69                     model.SubBottonIndex(subbottonindex);
 70 
 71                     model.isEditMenu(true);
 72                     var data =http://www.mamicode.com/ JSON.stringify(obj);
 73                     model.Menu(JSON.parse(data));
 74                     MenuFormValidate();
 75                 },
 76                 AddMenu: function (index) {//添加菜单
 77                     model.BottonIndex(-1);
 78                     model.SubBottonIndex(-1);
 79 
 80                     model.isEditMenu(false);
 81                     model.MenuIndex(index);
 82                     var menu = { type: "view", name: "", value: "" };
 83                     model.Menu(menu);
 84                     MenuFormValidate();
 85                 },
 86                 DeleteMenu: function () {//删除菜单
 87                     $(model.Menus().button).each(function (index, item) {
 88                         if (index === model.BottonIndex() && model.SubBottonIndex() === -1) {
 89                             model.Menus().button.splice(index, 1);
 90                         }
 91 
 92                         if (item.sub_button instanceof Array) {
 93                             $(item.sub_button).each(function (index1) {
 94                                 if (index === model.BottonIndex() && index1 === model.SubBottonIndex()) {
 95                                     item.sub_button.splice(index1, 1);
 96                                 }
 97                             });
 98                         }
 99                     });
100                     model.Menu(undefined);
101                     model.MenuIndex(undefined);
102                     model.BottonIndex(-1);
103                     model.SubBottonIndex(-1);
104                     model.MenusReset();
105                 },
106                 CancelMenuSave: function () {//取消编辑,重置参数
107                     model.Menu(undefined);
108                     model.MenuIndex(undefined);
109                     model.BottonIndex(-1);
110                     model.SubBottonIndex(-1);
111                 },
112                 MenuSave: function () {//保存编辑的菜单
113                     if (!$("#MenuForm").data("validator").form()) {
114                         return;
115                     }
116 
117                     if (model.isEditMenu()) {
118                         var menuIndex = model.BottonIndex();
119                         var subMenuIndex = model.SubBottonIndex();
120                         if (subMenuIndex === -1) {
121                             model.Menus().button[menuIndex] = model.Menu();
122                         } else {
123                             model.Menus().button[menuIndex].sub_button[subMenuIndex] = model.Menu();
124                         }
125                     } else {
126                         if (model.MenuIndex() != undefined) {
127                             if (model.Menus().button[model.MenuIndex()].sub_button == undefined) {
128                                 model.Menus().button[model.MenuIndex()].sub_button = new Array();
129                             }
130                             model.Menus().button[model.MenuIndex()].sub_button.unshift(model.Menu());
131                         } else {
132                             model.Menus().button.push(model.Menu());
133                         }
134                     }
135 
136                     model.Menu(undefined);
137                     model.MenuIndex(undefined);
138                     model.BottonIndex(-1);
139                     model.SubBottonIndex(-1);
140                     model.MenusReset();
141                 },

 

  绑定好监控属性,生成菜单排版

 1 <div class="panel-body" data-bind="with:Menus" id="divMenu" style="display: none;">
 2         <div style="height: 200px;" data-bind="foreach:newArray(3)">
 3             <div class="list-group col-xs-4 clearFill bn">
 4                 <!--ko if:($parent.button.length>0 && $parent.button[$index()]!=undefined && $parent.button[$index()].sub_button!=undefined ) -->
 5                 <!--ko foreach:newArray((4-$parent.button[$index()].sub_button.length)) -->
 6                 <div class="list-group-item bn"></div>
 7                 <!--/ko-->
 8                 <!--ko if:$parent.button[$index()].sub_button.length<5 -->
 9                 <div class="list-group-item" data-bind="click:function (){$root.AddMenu($index())}"><i class="fa fa-plus"></i>
10                 </div>
11                 <!--/ko-->
12                 <!--ko foreach:($parent.button[$index()].sub_button) -->
13                 <div class="list-group-item" data-bind="text:name,attr:{‘bottonIndex‘:$parent.value,‘subbottonIndex‘:$index()},click:function (){$root.EditMenu($data,$parent.value,$index())}"></div>
14                 <!--/ko-->
15                 <!--/ko -->
16                 <!--ko if: $parent.button[$index()]!=undefined && $parent.button[$index()].sub_button==undefined -->
17                 <div class="list-group-item bn"></div>
18                 <div class="list-group-item bn"></div>
19                 <div class="list-group-item bn"></div>
20                 <div class="list-group-item bn"></div>
21                 <div class="list-group-item" data-bind="click:function (){$root.AddMenu($index())}"><i class="fa fa-plus"></i>
22                 </div>
23                 <!--/ko-->
24                 <!--ko if: $parent.button[$index()]==undefined -->
25                 <div class="list-group-item bn"></div>
26                 <div class="list-group-item bn"></div>
27                 <div class="list-group-item bn"></div>
28                 <div class="list-group-item bn"></div>
29                 <div class="list-group-item bn"></div>
30                 <!--/ko-->
31             </div>
32         </div>
33         <!--ko foreach:button -->
34         <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>
35         <!--/ko-->
36         <!--ko if:button.length < 3 -->
37         <div class="col-xs-4 list-group-item" data-bind="click:function (){$root.AddMenu();}"><i class="fa fa-plus"></i>
38         </div>
39         <!--/ko-->
40         <div class="clearfix"></div>
41 
42         <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)">
43             <form id="MenuForm" onsubmit="return false;">
44                 <div class="form-group col-xs-4">
45                     <input type="text" class="form-control" name="name" placeholder="请输入名称" data-bind="value:name">
46                 </div>
47                 <div class="form-group col-xs-4">
48                     <select class="form-control" onchange="$(‘#txtMenuButtonValue‘)
49     .attr(‘placeholder‘, $(this).find(‘option:selected‘).attr(‘pl‘))" data-bind="value:type">
50                         <option value="view" pl="请输入Url">跳转URL</option>
51                         <option value="click" pl="请输入Key">点击推事件</option>
52                         <option value="scancode_push" pl="请输入Key">扫码推事件</option>
53                         <option value="scancode_waitmsg" pl="请输入Key">扫码推事件且弹出“消息接收中”提示框</option>
54                         <option value="pic_sysphoto" pl="请输入Key">弹出系统拍照发图</option>
55                         <option value="pic_photo_or_album" pl="请输入Key">弹出拍照或者相册发图</option>
56                         <option value="pic_weixin" pl="请输入Key"> 弹出微信相册发图器</option>
57                         <option value="location_select" pl="请输入Key">弹出地理位置选择器</option>
58                     </select>
59                 </div>
60                 <div class="form-group col-xs-8">
61                     <input type="text" id="txtMenuButtonValue" name="value" class="form-control" placeholder="请输入Url" data-bind="value:value">
62                 </div>
63                 <div class="form-group col-xs-12">
64                     <button type="submit" class="btn btn-primary" data-bind="click:$root.MenuSave">确定</button>
65                     <button type="submit" class="btn btn-danger" data-bind="visible:$root.isEditMenu,click:$root.DeleteMenu">删除</button>
66                     <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>
67                     <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>
68                     <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>
69                     <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>
70                     <button type="button" class="btn btn-default" title="复制菜单" data-bind="visible:$root.isEditMenu(),click:$root.Copy">复制</button>
71                     <button type="button" class="btn btn-default" title="粘贴菜单" data-bind="click:$root.Paste">粘贴</button>
72                     <button type="submit" class="btn btn-default" data-bind="click:$root.CancelMenuSave">关闭</button>
73                 </div>
74             </form>
75         </div>
76         <div class="clearfix"></div>
77     </div>

 

  最后增加菜单的查询函数及发布函数。因为编辑菜单方便,菜单对象和微信自定义菜单接口所需要的json格式不对应,所以在查询现有菜单和发布菜单时,需要对json数据进行一下格式变化。

  1 ,
  2                 EditMenus: function (isQuery) {
  3                     if (isQuery == undefined) {
  4                         var menu = {};
  5                         menu.button = new Array();
  6                         model.Menus(menu);
  7                     } else {
  8                         var appId = model.AppId();
  9                         var appSecret = model.AppSecret();
 10                         var accessToken = model.AccessToken();
 11                         var type = model.Type();
 12                         var tokenType = model.TokenType();
 13                         var corpId = model.CorpId();
 14                         var permanentCode = model.PermanentCode();
 15                         var agentId = model.AgentId();
 16                         var suiteId = model.SuiteId();
 17                         var suiteSecret = model.SuiteSecret();
 18                         var suiteTicket = model.SuiteTicket();
 19 
 20                         if (type === "1" && tokenType === "2") {
 21                             if (appId == undefined || $.trim(appId).length === 0) {
 22                                 alert("请输入AppId");
 23                                 return;
 24                             }
 25 
 26                             if (appSecret == undefined || $.trim(appSecret).length === 0) {
 27                                 alert("请输入AppSecret");
 28                                 return;
 29                             }
 30                         } else if (type === "2" && tokenType === "2") {
 31                             if (corpId == undefined || $.trim(corpId).length === 0) {
 32                                 alert("请输入CorpId");
 33                                 return;
 34                             }
 35                             if (permanentCode == undefined || $.trim(permanentCode).length === 0) {
 36                                 alert("请输入永久授权码");
 37                                 return;
 38                             }
 39 
 40                             if (agentId == undefined || $.trim(agentId).length === 0) {
 41                                 alert("请输入AgentId");
 42                                 return;
 43                             }
 44 
 45                             if (suiteId == undefined || $.trim(suiteId).length === 0) {
 46                                 alert("请输入SuiteId");
 47                                 return;
 48                             }
 49 
 50                             if (suiteSecret == undefined || $.trim(suiteSecret).length === 0) {
 51                                 alert("请输入SuiteSecret");
 52                                 return;
 53                             }
 54 
 55                             if (suiteTicket == undefined || $.trim(suiteTicket).length === 0) {
 56                                 alert("请输入SuiteTicket");
 57                                 return;
 58                             }
 59                         } else if (tokenType === "1") {
 60                             if (accessToken == undefined || $.trim(accessToken).length === 0) {
 61                                 alert("请输入AccessToken");
 62                                 return;
 63                             }
 64                         }
 65                         $("#btnQueryMenu").button("查询中...");
 66                         $.ajax({
 67                             url: "",
 68                             datatype: "JSON",
 69                             type: "POST",
 70                             async: true,
 71                             data: JSON.stringify({
 72                                 appId: appId, appSecret: appSecret, accessToken: accessToken, type: type, tokenType: tokenType, corpId: corpId, permanentCode: permanentCode, agentId: agentId,
 73                                 suiteId: suiteId, suiteSecret: suiteSecret, suiteTicket: suiteTicket
 74                             }),
 75                             contentType: "application/json; charset=UTF-8",
 76                             success: function (obj) {
 77                                 $("#btnQueryMenu").button("reset");
 78                                 if (obj.Success) {
 79                                     var data =http://www.mamicode.com/ obj.Data;
 80                                     var menus = JSON.parse(data).menu;
 81                                     $(menus.button).each(function (index, item) {
 82                                         if (item.type === "view") {
 83                                             item.value =http://www.mamicode.com/ item.url;
 84                                             delete item.url;
 85                                         } else {
 86                                             item.value =http://www.mamicode.com/ item.key;
 87                                             delete item.key;
 88                                         }
 89                                         if (item.type == undefined) {
 90                                             item.type = "view";
 91                                             item.valuehttp://www.mamicode.com/= "";
 92                                         }
 93 
 94                                         if (item.sub_button instanceof Array) {
 95                                             $(item.sub_button).each(function (index1, item2) {
 96                                                 if (item2.type === "view") {
 97                                                     item2.value =http://www.mamicode.com/ item2.url;
 98                                                     delete item2.url;
 99                                                 } else {
100                                                     item2.value =http://www.mamicode.com/ item2.key;
101                                                     delete item2.key;
102                                                 }
103                                             });
104                                         }
105                                     });
106 
107                                     model.Menu(undefined);
108                                     model.MenuIndex(undefined);
109                                     model.BottonIndex(-1);
110                                     model.SubBottonIndex(-1);
111                                     model.Menus(undefined);
112                                     model.Menus(menus);
113                                 } else {
114                                     alert(obj.Messages);
115                                 }
116                             },
117                             error: function (xmlHttpRequest, textStatus, errorThrown) {
118                                 $("#btnQueryMenu").button("reset");
119                                 console.error(errorThrown);
120                             }
121                         });
122                     }
123                 },
124                 SaveMenus: function () {
125                     var menus = JSON.parse(JSON.stringify(model.Menus()));
126                     $(menus.button).each(function (index, item) {
127                         if (item.type === "view") {
128                             item.url = item.value;
129                             delete item.value;
130                         } else {
131                             item.key = item.value;
132                             delete item.value;
133                         }
134 
135                         if (item.sub_button instanceof Array) {
136                             $(item.sub_button).each(function (index1, item2) {
137                                 if (item2.type === "view") {
138                                     item2.url = item2.value;
139                                     delete item2.value;
140                                 } else {
141                                     item2.key = item2.value;
142                                     delete item2.value;
143                                 }
144                             });
145 
146                             if (item.sub_button.length > 0) {
147                                 delete item.key;
148                                 delete item.url;
149                                 delete item.type;
150                             } else {
151                                 delete item.sub_button;
152                             }
153                         }
154                     });
155 
156                     console.log(JSON.stringify(menus));
157 
158                     var appId = model.AppId();
159                     var appSecret = model.AppSecret();
160                     var accessToken = model.AccessToken();
161                     var type = model.Type();
162                     var tokenType = model.TokenType();
163                     var agentId = model.AgentId();
164                     var suiteId = model.SuiteId();
165                     var suiteSecret = model.SuiteSecret();
166                     var suiteTicket = model.SuiteTicket();
167 
168                     if (type === "1" && tokenType === "2") {
169                         if (appId == undefined || $.trim(appId).length === 0) {
170                             alert("请输入AppId");
171                             return;
172                         }
173 
174                         if (appSecret == undefined || $.trim(appSecret).length === 0) {
175                             alert("请输入AppSecret");
176                             return;
177                         }
178                     } else if (type === "2" && tokenType === "2") {
179                         if (agentId == undefined || $.trim(agentId).length === 0) {
180                             alert("请输入AgentId");
181                             return;
182                         }
183 
184                         if (suiteId == undefined || $.trim(suiteId).length === 0) {
185                             alert("请输入SuiteId");
186                             return;
187                         }
188 
189                         if (suiteSecret == undefined || $.trim(suiteSecret).length === 0) {
190                             alert("请输入SuiteSecret");
191                             return;
192                         }
193 
194                         if (suiteTicket == undefined || $.trim(suiteTicket).length === 0) {
195                             alert("请输入SuiteTicket");
196                             return;
197                         }
198                     } else if (tokenType === "1") {
199                         if (accessToken == undefined || $.trim(accessToken).length === 0) {
200                             alert("请输入AccessToken");
201                             return;
202                         }
203                     }
204 
205                     $("#btnSubmitMenu").button("发布中...");
206                     $.ajax({
207                         url: "",
208                         datatype: "JSON",
209                         type: "POST",
210                         async: true,
211                         data: JSON.stringify({
212                             appId: appId, appSecret: appSecret, accessToken: accessToken, type: type, tokenType: tokenType, agentId: agentId,
213                             suiteId: suiteId, suiteSecret: suiteSecret, suiteTicket: suiteTicket, menu: JSON.stringify(menus)
214                         }),
215                         contentType: "application/json; charset=UTF-8",
216                         success: function (obj) {
217                             $("#btnSubmitMenu").button("reset");
218                             if (obj.Success) {
219                                 alert("发布成功");
220                             } else {
221                                 alert(obj.Messages);
222                             }
223                         },
224                         error: function (xmlHttpRequest, textStatus, errorThrown) {
225                             $("#btnSubmitMenu").button("reset");
226                             console.error(errorThrown);
227                         }
228                     });
229                 }

 

  最终效果如下

技术分享

   在线体验可点击:在线体验Demo

   后台代码就不贴了c#开发可用Senparc.Weixin库轻松调用微信接口

  第一次写博客,质量不高,见谅见谅~

使用asp.net mvc,boostrap及knockout.js开发微信自定义菜单编辑工具