系列目錄html
一、若是不借用Senparc.Weixin SDK自定義菜單,編碼起來,工做量是很是之大前端
二、可是藉助SDK彷佛一切都是簡單得不要不要的ajax
三、自定義菜單無須要創建數據庫表數據庫
四、自定義菜單最多包括3個一級菜單,每一個一級菜單最多包含5個二級菜單。json
五、一級菜單最多4個漢字,二級菜單最多7個漢字,多出來的部分將會以「...」代替。api
六、建立自定義菜單後,菜單的刷新策略是,在用戶進入公衆號會話頁或公衆號profile頁時,若是發現上一次拉取菜單的請求在5分鐘之前,就會拉取一下菜單,若是菜單有更新,就會刷新客戶端的菜單。服務器
測試時能夠嘗試取消關注公衆帳號後再次關注,則能夠看到建立後的效果。微信
七、下載尾部代碼,跑起來調試mvc
一、click:點擊推事件用戶點擊click類型按鈕後,微信服務器會經過消息接口推送消息類型爲event的結構給開發者(參考消息接口指南),而且帶上按鈕中開發者填寫的key值,開發者能夠經過自定義的key值與用戶進行交互;app
二、view:跳轉URL用戶點擊view類型按鈕後,微信客戶端將會打開開發者在按鈕中填寫的網頁URL,可與網頁受權獲取用戶基本信息接口結合,得到用戶基本信息。
三、scancode_push:掃碼推事件用戶點擊按鈕後,微信客戶端將調起掃一掃工具,完成掃碼操做後顯示掃描結果(若是是URL,將進入URL),且會將掃碼的結果傳給開發者,開發者能夠下發消息。
四、scancode_waitmsg:掃碼推事件且彈出「消息接收中」提示框用戶點擊按鈕後,微信客戶端將調起掃一掃工具,完成掃碼操做後,將掃碼的結果傳給開發者,同時收起掃一掃工具,而後彈出「消息接收中」提示框,隨後可能會收到開發者下發的消息。
五、pic_sysphoto:彈出系統拍照發圖用戶點擊按鈕後,微信客戶端將調起系統相機,完成拍照操做後,會將拍攝的相片發送給開發者,並推送事件給開發者,同時收起系統相機,隨後可能會收到開發者下發的消息。
六、pic_photo_or_album:彈出拍照或者相冊發圖用戶點擊按鈕後,微信客戶端將彈出選擇器供用戶選擇「拍照」或者「從手機相冊選擇」。用戶選擇後即走其餘兩種流程。
七、pic_weixin:彈出微信相冊發圖器用戶點擊按鈕後,微信客戶端將調起微信相冊,完成選擇操做後,將選擇的相片發送給開發者的服務器,並推送事件給開發者,同時收起相冊,隨後可能會收到開發者下發的消息。
八、location_select:彈出地理位置選擇器用戶點擊按鈕後,微信客戶端將調起地理位置選擇工具,完成選擇操做後,將選擇的地理位置發送給開發者的服務器,同時收起位置選擇工具,隨後可能會收到開發者下發的消息。
九、media_id:下發消息(除文本消息)用戶點擊media_id類型按鈕後,微信服務器會將開發者填寫的永久素材id對應的素材下發給用戶,永久素材類型能夠是圖片、音頻、視頻、圖文消息。請注意:永久素材id必須是在「素材管理/新增永久素材」接口上傳後得到的合法id。
十、view_limited:跳轉圖文消息URL用戶點擊view_limited類型按鈕後,微信客戶端將打開開發者在按鈕中填寫的永久素材id對應的圖文消息URL,永久素材類型只支持圖文消息。請注意:永久素材id必須是在「素材管理/新增永久素材」接口上傳後得到的合法id。
總結出類型:
<select id="buttonDetails_type" class="dllButtonDetails"> <option value="click" selected="selected">點擊事件(傳回服務器)</option> <option value="view">訪問網頁(直接跳轉)</option> <option value="location_select">彈出地理位置選擇器</option> <option value="pic_photo_or_album">彈出拍照或者相冊發圖</option> <option value="pic_sysphoto">彈出系統拍照發圖</option> <option value="pic_weixin">彈出微信相冊發圖器</option> <option value="scancode_push">掃碼推事件</option> <option value="scancode_waitmsg">掃碼推事件且彈出「消息接收中」提示框</option> </select>
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141013&token=&lang=zh_CN
因爲不須要數據庫,因此只有控制器和前端
[HttpPost] public ActionResult CreateMenu( GetMenuResultFull resultFull, MenuMatchRule menuMatchRule) { WC_OfficalAccountsModel model = account_BLL.GetCurrentAccount(); string token = model.AccessToken; var useAddCondidionalApi = menuMatchRule != null && !menuMatchRule.CheckAllNull(); var apiName = string.Format("使用接口:{0}。", (useAddCondidionalApi ? "個性化菜單接口" : "普通自定義菜單接口")); try { //從新整理按鈕信息 WxJsonResult result = null; IButtonGroupBase buttonGroup = null; if (useAddCondidionalApi) { //個性化接口 buttonGroup = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetMenuFromJsonResult(resultFull, new ConditionalButtonGroup()).menu; var addConditionalButtonGroup = buttonGroup as ConditionalButtonGroup; addConditionalButtonGroup.matchrule = menuMatchRule; result = Senparc.Weixin.MP.CommonAPIs.CommonApi.CreateMenuConditional(token, addConditionalButtonGroup); apiName += string.Format("menuid:{0}。", (result as CreateMenuConditionalResult).menuid); } else { //普通接口 buttonGroup = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetMenuFromJsonResult(resultFull, new ButtonGroup()).menu; result = Senparc.Weixin.MP.CommonAPIs.CommonApi.CreateMenu(token, buttonGroup); } var json = new { Success = result.errmsg == "ok", Message = "菜單更新成功。" + apiName }; return Json(json); } catch (Exception ex) { var json = new { Success = false, Message = string.Format("更新失敗:{0}。{1}", ex.Message, apiName) }; return Json(json); } } public ActionResult GetMenu() { WC_OfficalAccountsModel model = account_BLL.GetCurrentAccount(); string token = model.AccessToken; var result = Senparc.Weixin.MP.CommonAPIs.CommonApi.GetMenu(token); if (result == null) { return Json(new { error = "菜單不存在或驗證失敗!" }, JsonRequestBehavior.AllowGet); } return Json(result, JsonRequestBehavior.AllowGet); } public ActionResult DeleteMenu() { try { WC_OfficalAccountsModel model = account_BLL.GetCurrentAccount(); string token = model.AccessToken; var result = Senparc.Weixin.MP.CommonAPIs.CommonApi.DeleteMenu(token); var json = new { Success = result.errmsg == "ok", Message = result.errmsg }; return Json(json, JsonRequestBehavior.AllowGet); } catch (Exception ex) { var json = new { Success = false, Message = ex.Message }; return Json(json, JsonRequestBehavior.AllowGet); } }
都用SDK來完成接口的調用
<style> .menutable { border: 1px #ccc solid; text-align: center; width: 1000px; line-height: 55px; } .menutable input[type="text"] { width: 150px; } .menutable th { border: 1px #ccc solid; text-align: center; } .menutable td { border: 1px #ccc solid; } .float-left { float: right; } .menu-state { line-height:40px; } </style> <div class="mvctool"> @Html.ToolButton("btnGetMenu", "fa fa-pencil", "獲取當前菜單", ref perm, "Edit", true) @Html.ToolButton("submitMenu", "fa fa-pencil", "更新到服務器", ref perm, "Edit", true) @Html.ToolButton("btnDeleteMenu", "fa fa-trash", "刪除菜單", ref perm, "Delete", true) <div class="rightdiv color-green"> 當前操做公衆號:<span id="CurrentOfficalAccount">@(account.OfficalName)</span> @{if (string.IsNullOrEmpty(account.AppId) || string.IsNullOrEmpty(account.AppSecret) || string.IsNullOrEmpty(account.AccessToken)) { <span class="color-red">當前公衆號沒有菜單功能</span> } } </div> </div> <form id="form_Menu" action="/WC/MenuSetting/CreateMenu" method="post"> <p class="displaynone"> 當前Token: <input id="tokenStr" name="token" class="control-input" style="width: 900px;" type="text" readonly="readonly" /><br /> </p> <p class="menu-state color-green"> 操做狀態:<strong id="menuState">-</strong> </p> <table> <tr> <td> <table class="formtable menutable" style="width:650px;"> <thead> <tr> <th></th> <th>第一列</th> <th>第二列</th> <th>第三列</th> </tr> </thead> <tbody> @for (int i = 0; i < 6; i++) { var isRootMenu = i == 5; <tr id="@(isRootMenu ? "subMenuRow_" + i : "rootMenuRow")"> <td> @(isRootMenu ? "一級菜單按鈕" : ("二級菜單No." + (i + 1))) </td> @for (int j = 0; j < 3; j++) { var namePrefix = isRootMenu ? string.Format("menu.button[{0}]", j) : string.Format("menu.button[{0}].sub_button[{1}]", j, i); var idPrefix = isRootMenu ? string.Format("menu_button{0}", j) : string.Format("menu_button{0}_sub_button{1}", j, i); <td> <input type="hidden" class="control-input" name="@(namePrefix).key" id="@(idPrefix)_key" /> <input type="hidden" class="control-input" name="@(namePrefix).type" id="@(idPrefix)_type" value="click" /> <input type="hidden" class="control-input" name="@(namePrefix).url" id="@(idPrefix)_url" /> <input type="text" class="control-input" name="@(namePrefix).name" id="@(idPrefix)_name" class="txtButton" data-i="@i" data-j="@j" @Html.Raw(isRootMenu ? string.Format(@"data-root=""{0}""", j) : "") /> </td> } </tr> } </tbody> </table> </td> <td style="width:500px"> <div id="buttonDetails"> <table class="formtable"> <tr> <th></th> <td> 按鈕其餘參數 </td> </tr> <tr> <th>Name:</th> <td><input type="text" id="buttonDetails_name" class="control-input txtButton" disabled="disabled" /></td> </tr> <tr> <th> Type: </th> <td> <select id="buttonDetails_type" class="dllButtonDetails"> <option value="click" selected="selected">點擊事件(傳回服務器)</option> <option value="view">訪問網頁(直接跳轉)</option> <option value="location_select">彈出地理位置選擇器</option> <option value="pic_photo_or_album">彈出拍照或者相冊發圖</option> <option value="pic_sysphoto">彈出系統拍照發圖</option> <option value="pic_weixin">彈出微信相冊發圖器</option> <option value="scancode_push">掃碼推事件</option> <option value="scancode_waitmsg">掃碼推事件且彈出「消息接收中」提示框</option> </select> </td> </tr> <tr id="buttonDetails_key_area"> <th> Key: </th> <td><input id="buttonDetails_key" class="control-input txtButtonDetails" type="text" /></td> </tr> <tr id="buttonDetails_url_area"> <th> Url: </th> <td> <input id="buttonDetails_url" class="control-input txtButtonDetails" type="text" /> </td> </tr> <tr> <th></th> <td> 若是還有下級菜單請忽略Type和Key、Url。 </td> </tr> </table> </div> </td> </tr> </table> <div id="addConditionalArea"> <p><h3>個性化菜單設置</h3></p> <table> <tr> <th>group_id</th> <td> <input type="text" name="MenuMatchRule.group_id" placeholder="用戶分組id,可經過用戶分組管理接口獲取" class="control-input" /> </td> </tr> <tr><th>sex</th><td><input type="text" name="MenuMatchRule.sex" placeholder="性別:男(1)女(2),不填則不作匹配" class="control-input" /></td></tr> <tr><th>country</th><td><input type="text" name="MenuMatchRule.country" placeholder="國家信息,是用戶在微信中設置的地區,具體請參考地區信息表" class="control-input" /></td></tr> <tr><th>province</th><td><input type="text" name="MenuMatchRule.province" placeholder="省份信息,是用戶在微信中設置的地區,具體請參考地區信息表" class="control-input" /></td></tr> <tr><th>city</th><td><input type="text" name="MenuMatchRule.city" placeholder="城市信息,是用戶在微信中設置的地區,具體請參考地區信息表" class="control-input" /></td></tr> <tr><th>client_platform_type</th><td><input type="text" name="MenuMatchRule.client_platform_type" placeholder="客戶端版本,當前只具體到系統型號:IOS(1), Android(2),Others(3),不填則不作匹配" class="control-input" /></td></tr> </table> <p><i>提示:若是全部字段都留空,則使用普通自定義菜單,若是任何一個條件有值,則使用個性化菜單接口</i></p> </div> <div class="clear"></div> </form> <div id="reveiveJSON" class="displaynone"> <p>接收菜單JSON:</p> <p><textarea id="txtReveiceJSON"></textarea></p> </div> <script src="@Url.Content("~/Scripts/WeChat/senparc.menu.js")"></script> <script> $(function () { senparc.menu.init(); }); </script>
var senparc = {}; var maxSubMenuCount = 5; var menuState; senparc.menu = { token: '', init: function () { menuState = $('#menuState'); $('#buttonDetails').hide(); $('#menuEditor').hide(); $("#buttonDetails_type").change(senparc.menu.typeChanged); $(':input[id^=menu_button]').click(function () { $('#buttonDetails').show(100); var idPrefix = $(this).attr('data-root') ? ('menu_button' + $(this).attr('data-root')) : ('menu_button' + $(this).attr('data-j') + '_sub_button' + $(this).attr('data-i')); var keyId = idPrefix + "_key"; var nameId = idPrefix + "_name"; var typeId = idPrefix + "_type"; var urlId = idPrefix + "_url"; var txtDetailsKey = $('#buttonDetails_key'); var txtDetailsName = $('#buttonDetails_name'); var ddlDetailsType = $('#buttonDetails_type'); var txtDetailsUrl = $('#buttonDetails_url'); var hiddenButtonKey = $('#' + keyId); var hiddenButtonType = $('#' + typeId); var hiddenButtonUrl = $('#' + urlId); txtDetailsKey.val(hiddenButtonKey.val()); txtDetailsName.val($('#' + nameId).val()); ddlDetailsType.val(hiddenButtonType.val()); txtDetailsUrl.val(hiddenButtonUrl.val()); senparc.menu.typeChanged(); txtDetailsKey.unbind('blur').blur(function () { hiddenButtonKey.val($(this).val()); }); ddlDetailsType.unbind('blur').blur(function () { hiddenButtonType.val($(this).val()); }); txtDetailsUrl.unbind('blur').blur(function () { hiddenButtonUrl.val($(this).val()); }); }); $('#menuLogin_Submit').click(function () { $.getJSON('/WC/MenuSetting/GetToken?t=' + Math.random(), { appId: $('#menuLogin_AppId').val(), appSecret: $('#menuLogin_AppSecret').val() }, function (json) { if (json.access_token) { senparc.menu.setToken(json.access_token); } else { alert(json.error || '執行過程有錯誤,請檢查!'); } }); }); $('#menuLogin_SubmitOldToken').click(function () { senparc.menu.setToken($('#menuLogin_OldToken').val()); }); $('#btnGetMenu').click(function () { menuState.html('獲取菜單中...'); $.getJSON('/WC/MenuSetting/GetMenu?t=' + Math.random(), { token: senparc.menu.token }, function (json) { if (json.menu) { $(':input[id^=menu_button]:not([id$=_type])').val(''); $('#buttonDetails:input').val(''); var buttons = json.menu.button; //此處i與j和頁面中反轉 for (var i = 0; i < buttons.length; i++) { var button = buttons[i]; $('#menu_button' + i + '_name').val(button.name); $('#menu_button' + i + '_key').val(button.key); $('#menu_button' + i + '_type').val(button.type || 'click'); $('#menu_button' + i + '_url').val(button.url); if (button.sub_button && button.sub_button.length > 0) { //二級菜單 for (var j = 0; j < button.sub_button.length; j++) { var subButton = button.sub_button[j]; var idPrefix = '#menu_button' + i + '_sub_button' + j; $(idPrefix + "_name").val(subButton.name); $(idPrefix + "_type").val(subButton.type || 'click'); $(idPrefix + "_key").val(subButton.key); $(idPrefix + "_url").val(subButton.url); } } else { //底部菜單 //... } } //顯示JSON $('#txtReveiceJSON').html(JSON.stringify(json)); menuState.html('菜單獲取已完成'); } else { menuState.html(json.error || '執行過程有錯誤,請檢查!'); } }); }); $('#btnDeleteMenu').click(function () { if (!confirm('肯定要刪除菜單嗎?此操做沒法撤銷!')) { return; } menuState.html('刪除菜單中...'); $.getJSON('/WC/MenuSetting/DeleteMenu?t=' + Math.random(), { token: senparc.menu.token }, function (json) { if (json.Success) { menuState.html('刪除成功,若是是誤刪,而且界面上有最新的菜單狀態,能夠當即點擊【更新到服務器】按鈕。'); } else { menuState.html(json.Message); } }); }); $('#submitMenu').click(function () { if (!confirm('肯定要提交嗎?此操做沒法撤銷!')) { return; } menuState.html('上傳中...'); $('#form_Menu').ajaxSubmit({ dataType: 'json', success: function (json) { if (json.Successed) { menuState.html('上傳成功'); } else { menuState.html(json.Message); } } }); }); }, typeChanged: function () { var val = $('#buttonDetails_type').val(); if (val.toUpperCase() == 'VIEW') { $('#buttonDetails_key_area').slideUp(100); $('#buttonDetails_url_area').slideDown(100); } else { $('#buttonDetails_key_area').slideDown(100); $('#buttonDetails_url_area').slideUp(100); } }, setToken: function (token) { senparc.menu.token = token; $('#tokenStr').val(token); $('#menuEditor').show(); $('#menuLogin').hide(); } };
1.普通菜單隻要關注了就能夠查看
2.個性化菜單是有查看條件,好比性別,那麼微信所屬人的性別對應才能夠查看
通常個性化菜單,適用於會員級別享有特殊權限
示例代碼下載:https://yunpan.cn/cM9ffkutawueD 訪問密碼 2f0d
參考開源項目中的Senparc.Weixin SDK,