菜单如何实现动态菜单?

image
就是图中的菜单是动态添加,就是每次在数据库里读取菜单,不在配置文件里读取,这个要如何实现,请求大神解答一下,谢谢

放到数据库里去,然后重写若干代码,我正在干这个事!

1 个赞

没有现成的示例,理论上是可以实现的。
如果只是需要简单地动态添加菜单,可扩展 AppMainWindow 类,然后使用这个类上定义的 mainMenu 实现动态添加菜单。
mainMenu.addMenuItem

大神给我贴个代码参考一下,感谢了大神

思路如下:
平台是读取的XML来生产菜单,为了尽量少侵入现有的代码,我现在是先从数据库读取后,然后产生XML的Document对象给平台来生成菜单,这样权限那边的代码可以不需要改。主要代码如下供参考:
1、定义了Menu实体用来在数据库里保存菜单和菜单项

@NamePattern("#getNP|code,caption")
@Table(name = "YDZH_MENU")
@Entity(name = "ydzh_Menu")
public class Menu  extends BaseEntity {
    private static final long serialVersionUID = -3388382443199117731L;

    @NotNull
    @Column(name = "CODE",unique = true, nullable = false,length = 32)
    protected String code;

    @Column(name = "CAPTION", length = 64)
    protected String caption;

    @Column(name = "ICON", length = 128)
    protected String icon;

    @Column(name = "DESCRIPTION", length = 128)
    protected String description;

    @Column(name = "STYLENAME", length = 128)
    protected String stylename;

    @Column(name = "SEPARATOR")
    protected Boolean separator;

    @Column(name = "INSERTBEFORE", length = 32)
    protected String insertBefore;

    @Column(name = "INSERTAFTER", length = 32)
    protected String insertAfter ;

    @Column(name = "SCREEN", length = 64)
    protected String screen;

    @Column(name = "BEAN", length = 64)
    protected String bean;

    @Column(name = "BEANMETHOD", length = 64)
    protected String beanMethod;

    @Column(name = "RUNCLASS", length = 64)
    protected String runClass;

    @Column(name = "RESIZABLE")
    protected Boolean resizable;

    @Column(name = "OPENTYPE", length = 8)
    protected String openType;

    @Column(name = "SHORTCUT", length = 32)
    protected String shortcut;

    @NotNull
    @Column(name = "ITEM", nullable = false)
    protected Boolean item=false;

    @Column(name = "SORT")
    protected Double sort=0D;

    @Lookup(type = LookupType.DROPDOWN)
    @OnDeleteInverse(DeletePolicy.DENY)
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "PARENT_ID")
    protected Menu parent;


    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getCaption() {
        return caption;
    }

    public void setCaption(String caption) {
        this.caption = caption;
    }

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getStylename() {
        return stylename;
    }

    public void setStylename(String stylename) {
        this.stylename = stylename;
    }

    public String getInsertBefore() {
        return insertBefore;
    }

    public void setInsertBefore(String insertBefore) {
        this.insertBefore = insertBefore;
    }

    public String getInsertAfter() {
        return insertAfter;
    }

    public void setInsertAfter(String insertAfter) {
        this.insertAfter = insertAfter;
    }

    public Menu getParent() {
        return parent;
    }

    public void setParent(Menu parent) {
        this.parent = parent;
    }

    public Boolean getSeparator() {
        return separator;
    }

    public void setSeparator(Boolean separator) {
        this.separator = separator;
    }

    public MenuItemOpenType getOpenType() {
        return openType==null?null: MenuItemOpenType.fromId(openType);
    }

    public void setOpenType(MenuItemOpenType openType) {
        this.openType = openType==null?null:openType.getId();
    }

    public String getScreen() {
        return screen;
    }

    public void setScreen(String screen) {
        this.screen = screen;
    }

    public String getBean() {
        return bean;
    }

    public void setBean(String bean) {
        this.bean = bean;
    }

    public String getBeanMethod() {
        return beanMethod;
    }

    public void setBeanMethod(String beanMethod) {
        this.beanMethod = beanMethod;
    }

    public String getRunClass() {
        return runClass;
    }

    public void setRunClass(String runClass) {
        this.runClass = runClass;
    }

    public Boolean getResizable() {
        return resizable;
    }

    public void setResizable(Boolean resizable) {
        this.resizable = resizable;
    }

    public String getShortcut() {
        return shortcut;
    }

    public void setShortcut(String shortcut) {
        this.shortcut = shortcut;
    }

    public Boolean getItem() {
        return item;
    }

    public void setItem(Boolean item) {
        this.item = item;
    }

    public Double getSort() {
        return sort;
    }

    public void setSort(Double sort) {
        this.sort = sort;
    }

    public  String getNP(){
        Messages messages= AppBeans.get("cuba_Messages");
        String result=messages.getMainMessage("menu-config."+ code);
        if (result.equals("menu-config."+ code))
            return caption;
        else
            return result;
    }

    public enum MenuItemOpenType implements EnumClass<String> {
        /**
         * Open a screen in new tab of the main window.
         * <br> In Web Client with {@code AppWindow.Mode.SINGLE} the new screen replaces current screen.
         */
        NEW_TAB("NEW_TAB"),
        /**
         * Open a screen on top of the current tab screens stack.
         */
        THIS_TAB("THIS_TAB"),
        /**
         * Open a screen as modal dialog.
         */
        DIALOG("DIALOG"),
        /**
         * In Desktop Client open a screen in new main window, in Web Client the same as new {@link #NEW_TAB}
         */
        NEW_WINDOW("NEW_WINDOW"),
        /**
         * In Web Client opens a screen as main
         */
        ROOT("ROOT");

        private String id;

        MenuItemOpenType(String id) {
            this.id = id;
        }

        @Override
        public String getId() {
            return id;
        }

        public static MenuItemOpenType fromId(String id) {
            for (MenuItemOpenType openType : MenuItemOpenType.values()) {
                if (openType.getId().equals(id))
                    return openType;
            }
            return null;
        }
    }

2、覆盖平台的MenuConfig类,覆盖checkInitialized、getItemCaption、init

public class DBMenuConfig extends MenuConfig {

    private final Logger log = LoggerFactory.getLogger(DBMenuConfig.class);

    private HashMap<String,String> menuCaptions=new HashMap<>();
    @Inject
    private DataManager dataManager;

    @Override
    protected void checkInitialized(){
        super.checkInitialized();
        //始终加载菜单
        initialized=false;
    }

    @Override
    public String getItemCaption(String id) {
        if (menuCaptions.containsKey(id))
            return menuCaptions.get(id);
        else
            return super.getItemCaption(id);
    }

    @Override
    protected void init() {
        rootItems.clear();
        menuCaptions.clear();

        String configName = AppContext.getProperty(MENU_CONFIG_XML_PROP);
        //去掉应用的本地菜单配置,改从数据库读取
        configName=configName.lastIndexOf(" ")>0?configName.substring(0,configName.lastIndexOf(" ")):"";
        if (!StringUtils.isEmpty(configName)) {
            StringTokenizer tokenizer = new StringTokenizer(configName);
            for (String location : tokenizer.getTokenArray()) {
                Resource resource = resources.getResource(location);
                if (resource.exists()) {
                    try (InputStream stream = resource.getInputStream()) {
                        Element rootElement = Dom4j.readDocument(stream).getRootElement();
                        loadMenuItems(rootElement, null);
                    } catch (IOException e) {
                        throw new RuntimeException("Unable to read menu config", e);
                    }
                } else {
                    log.warn("Resource {} not found, ignore it", location);
                }
            }
        }

        try {
            //获取根菜单
            List<KeyValueEntity> menus=dataManager.loadValues("select e.id, e.code,e.caption, e.icon,e.description,e.stylename,e.separator,e.insertBefore,e.insertAfter from ydzh_Menu e where e.item=false and e.parent.id in (select p.id from ydzh_Menu p where p.parent is null)  order by e.sort, e.createTs")
                    .properties("id","code","caption","icon","description","stylename","separator","insertBefore","insertAfter")
                    .list();
            if (menus.size()>0) {
                Document document = DocumentHelper.createDocument();
                Element config = document.addElement("menu-config");
                for (KeyValueEntity e : menus) {
                    Boolean separator= (Boolean) e.getValue("separator");
                    if (separator!=null && separator){
                        config.addElement("separator");
                    }
                    Element menu = config.addElement("menu");
                    String id= (String) e.getValue("code");
                    menu.addAttribute("id",id);
                    String caption = (String) e.getValue("caption");
                    if (!StringUtils.isEmpty(caption))
                       menuCaptions.put(id,caption);
                    String icon = (String) e.getValue("icon");
                    if (!StringUtils.isEmpty(icon))
                        menu.addAttribute("icon", icon);
                    String description = (String) e.getValue("description");
                    if (!StringUtils.isEmpty(description))
                        menu.addAttribute("description", description);
                    String stylename = (String) e.getValue("stylename");
                    if (!StringUtils.isEmpty(stylename))
                        menu.addAttribute("stylename", stylename);
                    String insertBefore = (String) e.getValue("insertBefore");
                    if (!StringUtils.isEmpty(insertBefore))
                        menu.addAttribute("insertBefore", insertBefore);
                    String insertAfter = (String) e.getValue("insertAfter");
                    if (!StringUtils.isEmpty(insertAfter))
                        menu.addAttribute("insertAfter", insertAfter);
                    UUID parentId=(UUID)e.getValue("id");
                    loadSubMenu(menu,parentId);
                }
                log.debug("菜单内容:\n"+ Dom4jUtils.toXMLString(document));
                //加载菜单
                loadMenuItems(document.getRootElement(), null);
            }

        } catch (Exception e) {
            throw new RuntimeException("Unable to read DBMenu config", e);
        }
    }

    private void loadSubMenu(Element parentMenu,UUID parentId){
        //获取菜单
        List<KeyValueEntity> menus=dataManager.loadValues("select e.id, e.code,e.caption,e.icon,e.description,e.stylename,e.separator,e.insertBefore,e.insertAfter,e.openType,e.screen,e.bean,e.beanMethod,e.runClass,e.item from ydzh_Menu e where e.parent.id=:parentId order by e.sort, e.createTs")
                .properties("id","code","caption","icon","description","stylename","separator","insertBefore","insertAfter","openType","screen","bean","beanMethod","runClass","item")
                .parameter("parentId",parentId)
                .list();
        for (KeyValueEntity e : menus) {
            Boolean isItem= (Boolean) e.getValue("item");
            Boolean separator= (Boolean) e.getValue("separator");
            if (separator!=null && separator){
                parentMenu.addElement("separator");
            }
            if (isItem){
                //菜单项
                Element item = parentMenu.addElement("item");
                String id= (String) e.getValue("code");
                item.addAttribute("id",id);
                String caption = (String) e.getValue("caption");
                if (!StringUtils.isEmpty(caption))
                    menuCaptions.put(id,caption);
                String icon = (String) e.getValue("icon");
                if (!StringUtils.isEmpty(icon))
                    item.addAttribute("icon", icon);
                String description = (String) e.getValue("description");
                if (!StringUtils.isEmpty(description))
                    item.addAttribute("description", description);
                String stylename = (String) e.getValue("stylename");
                if (!StringUtils.isEmpty(stylename))
                    item.addAttribute("stylename", stylename);
                String insertBefore = (String) e.getValue("insertBefore");
                if (!StringUtils.isEmpty(insertBefore))
                    item.addAttribute("insertBefore", insertBefore);
                String insertAfter = (String) e.getValue("insertAfter");
                if (!StringUtils.isEmpty(insertAfter))
                    item.addAttribute("insertAfter", insertAfter);
                String openType = (String) e.getValue("openType");
                if (!StringUtils.isEmpty(openType))
                    item.addAttribute("openType", openType);
                String screen = (String) e.getValue("screen");
                if (!StringUtils.isEmpty(screen))
                    item.addAttribute("screen", screen);
                String bean = (String) e.getValue("bean");
                if (!StringUtils.isEmpty(bean))
                    item.addAttribute("bean", bean);
                String beanMethod = (String) e.getValue("beanMethod");
                if (!StringUtils.isEmpty(beanMethod))
                    item.addAttribute("beanMethod", beanMethod);
                String runClass = (String) e.getValue("runClass");
                if (!StringUtils.isEmpty(runClass))
                    item.addAttribute("class", runClass);
            }else{
                //菜单
                Element menu = parentMenu.addElement("menu");
                String id= (String) e.getValue("code");
                menu.addAttribute("id",id);
                String caption = (String) e.getValue("caption");
                if (!StringUtils.isEmpty(caption))
                    menuCaptions.put(id,caption);
                String icon = (String) e.getValue("icon");
                if (!StringUtils.isEmpty(icon))
                    menu.addAttribute("icon", icon);
                String description = (String) e.getValue("description");
                if (!StringUtils.isEmpty(description))
                    menu.addAttribute("description", description);
                String stylename = (String) e.getValue("stylename");
                if (!StringUtils.isEmpty(stylename))
                    menu.addAttribute("stylename", stylename);
                String insertBefore = (String) e.getValue("insertBefore");
                if (!StringUtils.isEmpty(insertBefore))
                    menu.addAttribute("insertBefore", insertBefore);
                String insertAfter = (String) e.getValue("insertAfter");
                if (!StringUtils.isEmpty(insertAfter))
                    menu.addAttribute("insertAfter", insertAfter);
                loadSubMenu(menu,(UUID)e.getValue("id"));
            }
        }
    }
}

简要说明:
1、覆盖checkInitialized是因为平台默认是启动加载一次菜单,现在改成多次加载以反应数据的变化;
2、覆盖getItemCaption是因为平台默认通过id读取配置文件来显示菜单名称,我在实体上增加了一个caption属性允许将菜单名称保存到数据库;
3、覆盖init是将原来从本地XML获取菜单数据改成从数据库获取,首先依然加载其它组件中通过XML定义的菜单,忽略本地的XML菜单(如不想忽略可屏蔽那行代码)。最后从数据库获取菜单项,按sort属性排序显示;
4、Menu表里有一条默认的根菜单main,对用户不可见;
5、需要在spring里配置一下使得bean生效,代码没有优化,供参考。

5 个赞

感谢大哥