使用asp.net mvc,boostrap及knockout.js開(kāi)發(fā)微信自定義菜單編輯工具(推薦)
前言
微信的接口調(diào)試工具可以編輯自定義菜單,不過(guò)是提交json格式數(shù)據(jù)創(chuàng)建菜單,非常的不方便還容易出錯(cuò)。網(wǎng)上的工具不好用,所以就自己寫(xiě)了一個(gè)。
正文
先用bootstrap排個(gè)頁(yè)面框架出來(lái),調(diào)用自定義菜單接口需要用到AccessToken,放個(gè)輸入框輸入AccessToken。也不排除想直接輸入AppId和AppSecret來(lái)獲取AccessToken的用戶,所以還需要下拉菜單來(lái)選擇是輸入AccessToken還是直接獲取AccessToken。為了兼顧微信企業(yè)號(hào)應(yīng)用創(chuàng)建菜單還需要AgentId,CorpId,套件永久授權(quán)碼,SuiteId,SuiteSecret,SuiteTicket,參數(shù)的輸入框大致就是這些。
使用knockout定義好observables監(jiān)控屬性。并綁定到輸入框上。

定義菜單展示及菜單編輯模塊,排版為微信公眾號(hào)菜單三個(gè)大菜單,每個(gè)大菜單下面可以配五個(gè)子菜單。大致思路如下,頁(yè)面排版為六行三列,三個(gè)大菜單未配置滿時(shí)在右側(cè)顯示增加菜單按鈕,
每個(gè)父級(jí)菜單的子菜單未配置滿時(shí)在上方顯示增加菜單按鈕。未配置滿時(shí)以空白div占位。
定義個(gè)函數(shù)生成自定義長(zhǎng)度數(shù)組

使用knockout定義好菜單監(jiān)控屬性,格式為
{
"button": [
{
"name": "父級(jí)菜單1",
"sub_button": [
{
"type": "view",
"name": "子菜單1",
"url": ""
}
]
},
{
"name": "父級(jí)菜單1",
"sub_button": [
{
"type": "view",
"name": "子菜單2",
"url": ""
},
{
"type": "view",
"name": "子菜單1",
"url": ""
}
]
}
]
}
定義添加,編輯,刪除菜單函數(shù),定義添加編輯菜單時(shí)臨時(shí)監(jiān)控屬性,定義當(dāng)前編輯菜單索引的監(jiān)控屬性。

一個(gè)一個(gè)編輯菜單還不是很方便,所以還要定義菜單的 上 下 左 右 的移動(dòng),及復(fù)制粘貼功能。
function MenuFormValidate() {
$("#MenuForm").validate({
rules: {
name: {
required: true
},
value: {
required: false
}
},
messages: {
name: {
required: "請(qǐng)輸入名稱"
},
value: {
required: $("#txtMenuButtonValue").attr("placeholder")
}
}
});
}
MenusReset:function () {
var menus = JSON.stringify(model.Menus());
model.Menus(undefined);
model.Menus(JSON.parse(menus));//刷新菜單對(duì)象
MenuFormValidate();//重新綁定驗(yàn)證方法
},
MenuIndex: ko.observable(), //父級(jí)菜單索引
isEditMenu: ko.observable(false), //是否是編輯菜單
BottonIndex: ko.observable(-1), //編輯菜單的父級(jí)菜單索引
SubBottonIndex: ko.observable(-1), //編輯菜單的子菜單索引
Menu: ko.observable(),//編輯菜單時(shí)臨時(shí)監(jiān)控屬性
CopyMenu: ko.observable(),//復(fù)制的菜單對(duì)象
Copy: function () { //復(fù)制
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 () {//向上移動(dòng)
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 () {//向下移動(dòng)
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 () {//向左移動(dòng)
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 () {//向右移動(dòng)
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 () {//取消編輯,重置參數(shù)
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();
},
綁定好監(jiān)控屬性,生成菜單排版
<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="請(qǐng)輸入名稱" 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="請(qǐng)輸入U(xiǎn)rl">跳轉(zhuǎn)URL</option>
<option value="click" pl="請(qǐng)輸入Key">點(diǎn)擊推事件</option>
<option value="scancode_push" pl="請(qǐng)輸入Key">掃碼推事件</option>
<option value="scancode_waitmsg" pl="請(qǐng)輸入Key">掃碼推事件且彈出“消息接收中”提示框</option>
<option value="pic_sysphoto" pl="請(qǐng)輸入Key">彈出系統(tǒng)拍照發(fā)圖</option>
<option value="pic_photo_or_album" pl="請(qǐng)輸入Key">彈出拍照或者相冊(cè)發(fā)圖</option>
<option value="pic_weixin" pl="請(qǐng)輸入Key"> 彈出微信相冊(cè)發(fā)圖器</option>
<option value="location_select" pl="請(qǐng)輸入Key">彈出地理位置選擇器</option>
</select>
</div>
<div class="form-group col-xs-8">
<input type="text" id="txtMenuButtonValue" name="value" class="form-control" placeholder="請(qǐng)輸入U(xiǎn)rl" 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="復(fù)制菜單" data-bind="visible:$root.isEditMenu(),click:$root.Copy">復(fù)制</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">關(guān)閉</button>
</div>
</form>
</div>
<div class="clearfix"></div>
</div>
最后增加菜單的查詢函數(shù)及發(fā)布函數(shù)。因?yàn)榫庉嫴藛畏奖悖藛螌?duì)象和微信自定義菜單接口所需要的json格式不對(duì)應(yīng),所以在查詢現(xiàn)有菜單和發(fā)布菜單時(shí),需要對(duì)json數(shù)據(jù)進(jìn)行一下格式變化。,
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("請(qǐng)輸入AppId");
return;
}
if (appSecret == undefined || $.trim(appSecret).length === 0) {
alert("請(qǐng)輸入AppSecret");
return;
}
} else if (type === "2" && tokenType === "2") {
if (corpId == undefined || $.trim(corpId).length === 0) {
alert("請(qǐng)輸入CorpId");
return;
}
if (permanentCode == undefined || $.trim(permanentCode).length === 0) {
alert("請(qǐng)輸入永久授權(quán)碼");
return;
}
if (agentId == undefined || $.trim(agentId).length === 0) {
alert("請(qǐng)輸入AgentId");
return;
}
if (suiteId == undefined || $.trim(suiteId).length === 0) {
alert("請(qǐng)輸入SuiteId");
return;
}
if (suiteSecret == undefined || $.trim(suiteSecret).length === 0) {
alert("請(qǐng)輸入SuiteSecret");
return;
}
if (suiteTicket == undefined || $.trim(suiteTicket).length === 0) {
alert("請(qǐng)輸入SuiteTicket");
return;
}
} else if (tokenType === "1") {
if (accessToken == undefined || $.trim(accessToken).length === 0) {
alert("請(qǐng)輸入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("請(qǐng)輸入AppId");
return;
}
if (appSecret == undefined || $.trim(appSecret).length === 0) {
alert("請(qǐng)輸入AppSecret");
return;
}
} else if (type === "2" && tokenType === "2") {
if (agentId == undefined || $.trim(agentId).length === 0) {
alert("請(qǐng)輸入AgentId");
return;
}
if (suiteId == undefined || $.trim(suiteId).length === 0) {
alert("請(qǐng)輸入SuiteId");
return;
}
if (suiteSecret == undefined || $.trim(suiteSecret).length === 0) {
alert("請(qǐng)輸入SuiteSecret");
return;
}
if (suiteTicket == undefined || $.trim(suiteTicket).length === 0) {
alert("請(qǐng)輸入SuiteTicket");
return;
}
} else if (tokenType === "1") {
if (accessToken == undefined || $.trim(accessToken).length === 0) {
alert("請(qǐng)輸入AccessToken");
return;
}
}
$("#btnSubmitMenu").button("發(fā)布中...");
$.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("發(fā)布成功");
} else {
alert(obj.Messages);
}
},
error: function (xmlHttpRequest, textStatus, errorThrown) {
$("#btnSubmitMenu").button("reset");
console.error(errorThrown);
}
});
}
最終效果如下

以上所述是小編給大家介紹的使用asp.net mvc,boostrap及knockout.js開(kāi)發(fā)微信自定義菜單編輯工具,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
- knockoutjs模板實(shí)現(xiàn)樹(shù)形結(jié)構(gòu)列表
- Asp.net MVC利用knockoutjs實(shí)現(xiàn)登陸并記錄用戶的內(nèi)外網(wǎng)IP及所在城市(推薦)
- KnockoutJS 3.X API 第四章之表單textInput、hasFocus、checked綁定
- KnockoutJS 3.X API 第四章之?dāng)?shù)據(jù)控制流foreach綁定
- KnockoutJS 3.X API 第四章之?dāng)?shù)據(jù)控制流with綁定
- KnockoutJS 3.X API 第四章之事件event綁定
- KnockoutJS 3.X API 第四章之表單value綁定
- BootstrapTable+KnockoutJS相結(jié)合實(shí)現(xiàn)增刪改查解決方案(三)兩個(gè)Viewmodel搞定增刪改查
- KnockoutJS數(shù)組比較算法實(shí)例詳解
相關(guān)文章
VS 2015開(kāi)發(fā)跨平臺(tái)手機(jī)應(yīng)用的配置教程
這篇文章主要給大家介紹了關(guān)于VS 2015開(kāi)發(fā)跨平臺(tái)手機(jī)應(yīng)用配置的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
asp.net基礎(chǔ)學(xué)習(xí)之前端頁(yè)面布局
這篇文章主要為大家詳細(xì)介紹了asp.net基礎(chǔ)學(xué)習(xí)之前端頁(yè)面布局,什么是母版頁(yè),如何創(chuàng)建母版頁(yè),感興趣的小伙伴們可以參考一下2016-08-08
Asp.Net套用母版頁(yè)后元素ID不一致(個(gè)人總結(jié))
這篇文章主要介紹了Asp.Net套用母版頁(yè)后元素ID不一致(個(gè)人總結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
.net core利用orm如何操作mysql數(shù)據(jù)庫(kù)詳解
這篇文章主要給大家介紹了關(guān)于.net core利用orm如何操作mysql數(shù)據(jù)庫(kù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
asp.net實(shí)現(xiàn)獲取客戶端詳細(xì)信息
這篇文章主要介紹了asp.net實(shí)現(xiàn)獲取客戶端詳細(xì)信息,包括客戶端瀏覽器及主機(jī)信息,感興趣的小伙伴們可以參考一下2015-12-12
asp.net導(dǎo)出excel的簡(jiǎn)單方法實(shí)例
這篇文章主要介紹了asp.net導(dǎo)出excel的簡(jiǎn)單方法實(shí)例,需要的朋友可以參考下2014-02-02
ASP.NET MVC用存儲(chǔ)過(guò)程批量添加修改數(shù)據(jù)操作
這篇文章主要介紹了ASP.NET MVC用存儲(chǔ)過(guò)程批量添加修改數(shù)據(jù)的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-02-02

