DIY menu

lanrion edited this page Apr 19, 2014 · 5 revisions

自定义菜单的实现

此实现测试通过的条件:

  • gem 'rails', '4.0.2'
  • gem 'jbuilder', '~> 1.2'

数据库设计:

class CreateDiymenus < ActiveRecord::Migration
  def change
    create_table :diymenus do |t|
      t.integer :public_account_id
      t.integer :parent_id # 所属父级菜单,如果当前是父级菜单,则此值为空
      t.string :name
      t.string :key  
      t.string :url
      t.boolean :is_show # 是否显示
      t.integer :sort    # 排序功能

      t.timestamps
    end
    add_index :diymenus, :public_account_id
    add_index :diymenus, :parent_id
    add_index :diymenus, :key

  end
end

Diymenu model 层设计

class Diymenu < ActiveRecord::Base

  CLICK_TYPE = "click" # key
  VIEW_TYPE  = "view"  # url

  belongs_to :public_account

  has_many :sub_menus, ->{where(is_show: true).order("sort").limit(5)}, class_name: "Diymenu", foreign_key: :parent_id

  def has_sub_menu?
    sub_menus.present?
  end

  # 优先为 click 类型
  def type
    key.present? ? CLICK_TYPE : VIEW_TYPE
  end

  def button_type(jbuilder)
    is_view_type? ? (jbuilder.url url) : (jbuilder.key key)
  end

  def is_view_type?
    type == VIEW_TYPE
  end
end

PublicAccount Model 设计

class PublicAccount < ActiveRecord::Base

# 自定义菜单
has_many :diymenus, dependent: :destroy

# 当前公众账号的所有父级菜单
has_many :parent_menus, ->{includes(:sub_menus).where(parent_id: nil, is_show: true).order("sort").limit(3)}, class_name: "Diymenu", foreign_key: :public_account_id

  def build_menu
    Jbuilder.encode do |json|
      json.button (parent_menus) do |menu|
        json.name menu.name
        if menu.has_sub_menu?
          json.sub_button(menu.sub_menus) do |sub_menu|
            json.type sub_menu.type
            json.name sub_menu.name
            sub_menu.button_type(json)
          end
        else
          json.type menu.type
          menu.button_type(json)
        end
      end
    end
  end
end

Action层的用法

# 结合: https://github.com/lanrion/weixin_authorize(建议选用此gem的Redis存access_token方案)
def generate_menu
  weixin_client = WeixinAuthorize::Client.new(@current_public_account.app_key, @current_public_account.app_secret)
  menu   = @current_public_account.build_menu
  result = weixin_client.create_menu(menu)
  set_error_message(result["errmsg"]) if result["errcode"] != 0
  redirect_to public_account_diymenus_path(@current_public_account)
end

需要存储公众账号的实现

Action 层:

if $weixin_authorize.is_valid?
  build_result = $weixin_authorize.create_menu(Diymenu.build_menu)
  msg = "build menu successfully." if build_result.ok?
else
  msg = "invalid appid or appsecret."
end
redirect_to admin_diymenus_path, notice: msg

Model层

class Diymenu < ActiveRecord::Base

  validates_uniqueness_of :name

  CLICK_TYPE = "click".freeze # key
  VIEW_TYPE  = "view".freeze  # url

  has_many :sub_menus, ->{where(is_show: true).order("sort").limit(5)}, class_name: "Diymenu", foreign_key: :parent_id

  belongs_to :parent, foreign_key: :parent_id, class_name: "Diymenu"

  def has_sub_menu?
    sub_menus.present?
  end

  # 优先为 click 类型
  def type
    key.present? ? CLICK_TYPE : VIEW_TYPE
  end

  def button_type(jbuilder)
    is_view_type? ? (jbuilder.url url) : (jbuilder.key key)
  end

  def is_view_type?
    type == VIEW_TYPE
  end

  class << self

    def parent_selection(current_menu)
      where.not(id: current_menu.id).map{|menu| [menu.name, menu.id]}
    end

    def parent_menus
      includes(:sub_menus).where(parent_id: nil, is_show: true).order("sort").limit(3)
    end

    def build_menu
      Jbuilder.encode do |json|
        json.button (parent_menus) do |menu|
          json.name menu.name
          if menu.has_sub_menu?
            json.sub_button(menu.sub_menus) do |sub_menu|
              json.type sub_menu.type
              json.name sub_menu.name
              sub_menu.button_type(json)
            end
          else
            json.type menu.type
            menu.button_type(json)
          end
        end
      end
    end
  end

end

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.