自定义菜单

简介

TNWX: TypeScript + Node.js + WeiXin 微信系开发脚手架,支持微信公众号、微信支付、微信小游戏、微信小程序、企业号/企业微信。最最最重要的是能快速的集成至任何 Node.js 框架(Express、Nest、Egg、Koa 等)

接口权限

公众号接口权限说明自定义菜单 - 图1

菜单规则以及按钮类型说明自定义菜单 - 图2

实现自定义菜单有两种方式

  • 编辑模式
  • 开发模式

编辑模式

登录 MP 平台,侧栏找到 添加功能插件 菜单并在插件库中找到 自定义菜单 插件添加后按照提示操作即可。

开发模式

开发模式下有两种实现方式但推荐使用第二种

TNWX 中实现方案

  • 创建菜单
  • 删除菜单
  • 查询菜单
  • 添加个性化菜单
  • 删除个性化菜单
  • 测试个性化菜单匹配结果
  1. export class MenuApi {
  2. private static createMenuUrl = 'https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s'
  3. private static deleteMenuUrl = 'https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s';
  4. private static getMenuUrl = 'https://api.weixin.qq.com/cgi-bin/menu/get?access_token=s%';
  5. private static getSelfMenuInfoUrl = 'https://api.weixin.qq.com/cgi-bin/get_current_selfmenu_info?access_token=s%';
  6. private static addConditionalUrl = 'https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token=s%';
  7. private static delConditionalUrl = 'https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token=s%';
  8. private static tryMatchUrl = 'https://api.weixin.qq.com/cgi-bin/menu/trymatch?access_token=s%';
  9. /**
  10. * 创建菜单
  11. * @param response
  12. * @param menuJson
  13. */
  14. public static async create(menuJson: string) {
  15. let accessToken = await AccessTokenApi.getAccessToken();
  16. let url = util.format(this.createMenuUrl, (<AccessToken>accessToken).getAccessToken);
  17. return HttpKit.getHttpDelegate.httpPost(url, menuJson);
  18. }
  19. /**
  20. * 删除菜单
  21. * @param response
  22. */
  23. public static async delete() {
  24. let accessToken = await AccessTokenApi.getAccessToken();
  25. let url = util.format(this.deleteMenuUrl, (<AccessToken>accessToken).getAccessToken);
  26. return HttpKit.getHttpDelegate.httpGet(url);
  27. }
  28. /**
  29. * 查询菜单
  30. * @param response
  31. */
  32. public static async get() {
  33. let accessToken = await AccessTokenApi.getAccessToken();
  34. let url = util.format(this.getMenuUrl, (<AccessToken>accessToken).getAccessToken);
  35. return HttpKit.getHttpDelegate.httpGet(url);
  36. }
  37. public static async getCurrentSelfMenu() {
  38. let accessToken = await AccessTokenApi.getAccessToken();
  39. let url = util.format(this.getSelfMenuInfoUrl, (<AccessToken>accessToken).getAccessToken);
  40. return HttpKit.getHttpDelegate.httpGet(url);
  41. }
  42. /**
  43. * 添加个性化菜单
  44. * @param response
  45. * @param menuJson
  46. */
  47. public static async addConditional(menuJson: string) {
  48. let accessToken = await AccessTokenApi.getAccessToken();
  49. let url = util.format(this.addConditionalUrl, (<AccessToken>accessToken).getAccessToken);
  50. return HttpKit.getHttpDelegate.httpPost(url, menuJson);
  51. }
  52. /**
  53. * 删除个性化菜单
  54. * @param response
  55. */
  56. public static async deleteConditional() {
  57. let accessToken = await AccessTokenApi.getAccessToken();
  58. let url = util.format(this.delConditionalUrl, (<AccessToken>accessToken).getAccessToken);
  59. return HttpKit.getHttpDelegate.httpGet(url);
  60. }
  61. /**
  62. * 测试个性化菜单匹配结果
  63. * @param response
  64. * @param openId
  65. */
  66. public static async tryMatch(openId: string) {
  67. let accessToken = await AccessTokenApi.getAccessToken();
  68. let url = util.format(this.tryMatchUrl, (<AccessToken>accessToken).getAccessToken);
  69. return HttpKit.getHttpDelegate.httpPost(url, JSON.stringify({
  70. "user_id": openId
  71. }));
  72. }
  73. }

读取配置文件来创建菜单

  1. // 读取配置文件来创建自定义菜单
  2. app.get('/creatMenu', (req: any, res: any) => {
  3. fs.readFile("./config/menu.json", function (err, data) {
  4. if (err) {
  5. console.log(err);
  6. return;
  7. }
  8. let fileData = data.toString();
  9. console.log(fileData);
  10. // res.send(fileData)
  11. MenuApi.create(fileData).then(data => {
  12. res.send(data);
  13. });
  14. });
  15. });

动态创建自定义菜单

  1. app.get('/dynamicCreatMenu', (req: any, res: any) => {
  2. MenuApi.create(JSON.stringify(MenuManager.getMenu())).then(data => {
  3. res.send(data);
  4. });
  5. });

菜单管理类

  1. export class MenuManager {
  2. static getMenu(): Menu {
  3. let btn11 = new ClickButton();
  4. btn11.setName = "微信相册发图";
  5. btn11.setType = "pic_weixin";
  6. btn11.setKey = "rselfmenu_1_1";
  7. let btn12 = new ClickButton();
  8. btn12.setName = "拍照或者相册发图";
  9. btn12.setType = "pic_photo_or_album";
  10. btn12.setKey = "rselfmenu_1_2";
  11. let btn13 = new ClickButton();
  12. btn13.setName = "系统拍照发图";
  13. btn13.setType = "pic_sysphoto";
  14. btn13.setKey = "rselfmenu_1_3";
  15. let btn21 = new ClickButton();
  16. btn21.setName = "扫码带提示";
  17. btn21.setType = "scancode_waitmsg";
  18. btn21.setKey = "rselfmenu_2_1";
  19. let btn22 = new ClickButton();
  20. btn22.setName = "扫码推事件";
  21. btn22.setType = "scancode_push";
  22. btn22.setKey = "rselfmenu_2_2";
  23. let btn23 = new ViewButton();
  24. btn23.setName = "Gitee";
  25. btn23.setType = "view";
  26. btn23.setUrl = "https://gitee.com/javen205";
  27. let btn31 = new ViewButton();
  28. btn31.setName = "IJPay";
  29. btn31.setType = "view";
  30. btn31.setUrl = "https://gitee.com/javen205/IJPay";
  31. let btn32 = new ClickButton();
  32. btn32.setName = "发送位置";
  33. btn32.setType = "location_select";
  34. btn32.setKey = "rselfmenu_3_2";
  35. let btn33 = new ViewButton();
  36. btn33.setName = "在线咨询";
  37. btn33.setType = "view";
  38. btn33.setUrl = "http://wpa.qq.com/msgrd?v=3&uin=572839485&site=qq&menu=yes";
  39. let btn34 = new ViewButton();
  40. btn34.setName = "我的博客";
  41. btn34.setType = "view";
  42. btn34.setUrl = "https://blog.javen.dev";
  43. let btn35 = new ClickButton();
  44. btn35.setName = "点击事件";
  45. btn35.setType = "click";
  46. btn35.setKey = "rselfmenu_3_5";
  47. let mainBtn1 = new ComButton();
  48. mainBtn1.setName = "发图";
  49. mainBtn1.setSubButton = [btn11, btn12, btn13];
  50. let mainBtn2 = new ComButton();
  51. mainBtn2.setName = "扫码";
  52. mainBtn2.setSubButton = [btn21, btn22, btn23];
  53. let mainBtn3 = new ComButton();
  54. mainBtn3.setName = "个人中心";
  55. mainBtn3.setSubButton = [btn31, btn32, btn33, btn34, btn35];
  56. let menu = new Menu();
  57. menu.setButton = [mainBtn1, mainBtn2, mainBtn3];
  58. return menu;
  59. }
  60. }

封装实体类

  1. export class Menu {
  2. private button!: Button[];
  3. private matchrule!: Matchrule;
  4. public get getButton(): Button[] {
  5. return this.button;
  6. }
  7. public set setButton(button: Button[]) {
  8. this.button = button;
  9. }
  10. public get getMatchrule(): Matchrule {
  11. return this.matchrule;
  12. }
  13. public set setMatchrule(matchrule: Matchrule) {
  14. this.matchrule = matchrule;
  15. }
  16. }
  17. export class Button {
  18. private name: string;
  19. private type: string;
  20. constructor(name?: string, type?: string) {
  21. this.name = name || '';
  22. this.type = type || '';
  23. }
  24. public get getName(): string {
  25. return this.name;
  26. }
  27. public set setName(name: string) {
  28. this.name = name;
  29. }
  30. public get getType(): string {
  31. return this.type;
  32. }
  33. public set setType(type: string) {
  34. this.type = type;
  35. }
  36. }
  37. export class ClickButton extends Button {
  38. private key: string;
  39. constructor(name?: string, type?: string, key?: string) {
  40. super(name, type);
  41. this.key = key || '';
  42. }
  43. public get getKey(): string {
  44. return this.key;
  45. }
  46. public set setKey(key: string) {
  47. this.key = key;
  48. }
  49. }
  50. export class ComButton extends Button {
  51. private sub_button: Button[];
  52. constructor(name?: string, type?: string, sub_button?: Button[]) {
  53. super(name, type);
  54. this.sub_button = sub_button || [];
  55. }
  56. public get getSubButton(): Button[] {
  57. return this.sub_button;
  58. }
  59. public set setSubButton(sub_button: Button[]) {
  60. this.sub_button = sub_button;
  61. }
  62. }
  63. export class MediaButton extends Button {
  64. private media_id: string;
  65. constructor(name?: string, type?: string, media_id?: string) {
  66. super(name, type);
  67. this.media_id = media_id || '';
  68. }
  69. public get getMediaId(): string {
  70. return this.media_id;
  71. }
  72. public set setMediaId(media_id: string) {
  73. this.media_id = media_id;
  74. }
  75. }
  76. export class ViewButton extends Button {
  77. private url: string;
  78. constructor(name?: string, type?: string, url?: string) {
  79. super(name, type);
  80. this.url = url || '';
  81. }
  82. public get getUrl(): string {
  83. return this.url;
  84. }
  85. public set setUrl(url: string) {
  86. this.url = url;
  87. }
  88. }
  89. export class Matchrule {
  90. //用户分组id,可通过用户分组管理接口获取
  91. private tag_id!: string;
  92. //性别:男(1)女(2),不填则不做匹配
  93. private sex!: string;
  94. //国家信息
  95. private country!: string;
  96. //省份信息
  97. private province!: string;
  98. //城市信息
  99. private city!: string;
  100. //客户端版本,当前只具体到系统型号:IOS(1), Android(2),Others(3),不填则不做匹配
  101. private client_platform_type!: string;
  102. //语言信息
  103. private language!: string;
  104. public get getTagId(): string {
  105. return this.tag_id;
  106. }
  107. public set setTagId(tag_id: string) {
  108. this.tag_id = tag_id;
  109. }
  110. public get getSex(): string {
  111. return this.sex;
  112. }
  113. public set setSex(sex: string) {
  114. this.sex = sex;
  115. }
  116. public get getCountry(): string {
  117. return this.country;
  118. }
  119. public set setCountry(country: string) {
  120. this.country = country;
  121. }
  122. public get getProvince(): string {
  123. return this.province;
  124. }
  125. public set setProvince(province: string) {
  126. this.province = province;
  127. }
  128. public get getCity(): string {
  129. return this.city;
  130. }
  131. public set setCity(city: string) {
  132. this.city = city;
  133. }
  134. public get getClientPlatformType(): string {
  135. return this.client_platform_type;
  136. }
  137. public set setClientPlatformType(client_platform_type: string) {
  138. this.client_platform_type = client_platform_type;
  139. }
  140. public get getLanguage(): string {
  141. return this.language;
  142. }
  143. public set setLanguage(language: string) {
  144. this.language = language;
  145. }
  146. }

Java 版本自定义菜单

微信公众号开发之自定义菜单自定义菜单 - 图5

开源推荐