menus - 菜单管理app

以前的实现

在uliweb设计中,许多内容都是分布式可配置的。但是菜单却不是这样。一般的uliweb网站有两类的菜单,一种是导航菜单,比如首页上用来区分不同的大的功能,一般只有一层;另一种是侧边栏菜单,用来显示每个大功能对应的详细功能,可以是多级,一般组织为树型。为了简单,以前对于第一类菜单就是在settings.ini中写一个配置,如:

[LAYOUT]
TITLE = _('Red Breast Demo')
PROJECT = _('Red Breast')
MENUS <= [
    ('home', _('Home'), '/'),
    ('admin', _('Admin'), '/user/view'),
]

其中MENUS就是菜单的定义。然后在模板中可以定义一个menu函数,需要传入一个参数current用来指示当前高亮的是哪个。如:

{{menu('home')}}

对于第二类菜单,会在某个功能的 func_layout.html 模板中定义这个功能下的所有菜单,如:

{{
menu_items = []
if request.user:
    menu_items.append(
        {
        'name': 'user',
        'title':_('Settings'),
        'subs':[
                {'name': 'information', 'title':_('Information'), 'link':'/user/view'},
                {'name': 'password', 'title':_('Change Password'), 'link':'/user/change_password'},
                ]
        },
    )
pass
}}

使用时,通过定义了一个submenu函数,将上面的menu_items传入,同时指定当然高亮的菜单参数。如上面的结构中,想要高亮 information 菜单,需要: {{submanu(menu_items, 'user', 'information')}}

从上面的实现可以看出虽然很简单,但是有以下几个问题:

  1. 定义方式不统一
  2. 定义是集中式的,不利于插件式的项目组织方式。比如増加或去除app之后,还需要将相应的菜单进行修改。并且因为菜单的定义是集中式的,所以这个文件有可能在同时进行多个app修改时会产生冲突。
  3. 对菜单的权限控制比较弱。上面是通过if语句来处理的,但是对于复杂情况就不方便处理。

因此为了让菜单定义,使用更独立,我在plugs中添加了menus app,对菜单的处理进行了新的设计。

新的实现

首先在menus app中定义基本的结构,主要分为:

  1. 启动处理。定义了初始化处理 (after_init_apps),它将会收集所有定义在settings.ini中的MENUS中的菜单项。然后根据菜单定义的结构,重新组合为树型结构。

  2. 菜单结构。菜单定义支持多棵树,每模树都是一个完整的菜单结构。每模树的根结点都有一个名字,以进行区分。每个菜单项也都有自已的名字。同一个结点的子结点不允许重名,但是不同结点的子结点可以重名。整个菜单数据将保存到一个有序字典变量中。所以第一层就是不同的菜单树的根结点。根结点本身不用来显示。通过 get_menu(name) 可以得到某个菜单树。同时 name 是用 / 分隔的,如: a/b 表示返回根为 a 的菜单树中的 nameb 的菜单项。

  3. 菜单的定义。菜单要定义在 settings.ini 中的MENUS中。基本格式为:

     [MENUS]
     name1 = {'title':'title', 'subs':[
          {'title':'title', 'link':'link'},
          {'title':'title', 'link':'link'},
         ]}
     name2 = {'parent':'name1', 'title':'title', 'link':'link'}
    

    基本格式为 name={} 。其中值的部分是一个dict,可以有以下属性:

    • parent 可以缺省。如果不是在subs中,如果不存在或值为空时,则表示它是一个根结点。如果有值,则表示它是parent的子菜单,此时parent可以是用 / 分隔的字符串。如果不带 / 则会将匹配上的第一个菜单项作为父结点,如果带 / 则会按树型结构层层匹配。因此,如果要匹配有重名的结点时,要使用带 / 的形式。
    • order 表示顺序值。缺省为900。在生成菜单树时,同一父结点下的菜单项将按order进行排序。
    • title 表示菜单显示名。
    • link 表示链接。对于父菜单应为空或不提供。
    • name 表示菜单名。对于key=value的定义,key将自动保存到 value['name'] 上。但是对于定义在subs中的子菜单,要显示定义name值。
    • subs 表示子菜单。可以通过定义parent来关联父子菜单,这种形式更适合将不同的app的菜单进行组合。对于一个app中的菜单,可以直接通过 subs 来定义子菜单,同时不需要定义parent。注意,在定义subs时需要指明它的name值。
  4. 显示菜单。为了通用,其实并不需要定义展示部分,可以由用户自已处理。通过 get_menu() 来返回菜单项即可,然后自已编写显示代码。不过为了方便,menus实现了缺省的菜单显示。它使用一个jquery的插件 navgoco 来显示菜单。在menus中提供了一个menu()函数,它可以生成navgoco所需要的html代码。为了方便引入navgoco所需要的js及初始化js代码,menus提供了inc_menu.html模板,可以让用户在需要的地方通过 {{include "inc_menu.html"}} 来引入相应的内容,并在模板上定义了 menu() 函数。在这个函数中使用了 menus 中的menu函数。但是为了通用,并不是直接引入,而是通过functions.menu来调用,这样可以让用户在需要的时候替換为自已的方法。

    在 inc_menu.html和menus中各有一个menu函数。menus的menu只是单纯的生成菜单结构的html代码。而inc_menu.html是为了在模板中提供更一致的调用接口和方便生成html以及javascript代码,它会调用menus中的函数。所以一般使用的是模板中的menu函数。

  5. 权限控制。除了上面必输的一些属性外,你还可以自已添加一些属性用来进行特殊的处理。在plugs的menu函数中,还提供了权限控制支持。它可以传入一个check参数,用来检查菜单项是否要显示(注意,这里只决定要不要显示,并不能防止用户直接输入url来访问),这是一个回调函数,参数是正在处理的菜单项,如果返回True表示可以查看,返回False为看不到。在menus app中实现了一个缺省的检查函数,它是 plugs.menus.default_validator,它可以检查菜单项定义中的roles和permissions属性,因此它要使用 uliweb.contrib.rbac这个app。这个缺省的检查函数将与用户传入的check函数一起生效,因此哪怕你不传check参数,default_validator也会生效。如果不想让它生效,要么自已写一个menu函数,不再使用menus提供的缺省实现,自然不会进行这样的处理;要么修改settings.ini中的配置MENUS_CONFIG,使用 validators <= [] 。注意 <= 是表示不进行自动合并,而是替換。

使用示例

  1. INSTALLED_APPS 中配置 plugs.menus

  2. 在对应的app下配置MENUS,如:

     [MENUS]
     main = {'subs':[
             {'name':'request', 'title':'我发起的审批', 'subs':[
                 {'name':'request_all', 'title':'全部'},
                 {'name':'request_unfinished', 'title':'未处理完毕'},
             ]},
         ]
     }
     process = {'parent':'main', 'title':'待处理的审批', 
             'link':'/review/list?status=0', 'roles':['superuser']}
    

    上面其实只定义了一个菜单树 - main,process的父菜单是 main,所以它将合并到main菜单中。在process菜单中还定义了一个roles,它表示只有超级用户才可以看到。

  3. 在将要显示菜单的模板中找一个地方添加 {{include "inc_menu.html"}}

  4. 在将要显示菜单的模板中显示菜单的地方添加 {{menu('main')}} 这样将显示main菜单。如果要指定选中的项,可以 {{menu('main', 'request/request_all')}} 这样在显示菜单时request_all菜单将自动高亮。并且我处理了菜单展示,如果活动菜单项是子菜单,则会自动将父菜单打开,而不是折叠状态。

显示效果为:

在此输入图片描述

转载于:https://my.oschina.net/limodou/blog/170021

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的 Python 界面菜单管理系统的示例代码,使用的是 SQLite 数据库: ```python import tkinter as tk import sqlite3 class MenuManager: def __init__(self, master): self.master = master master.title('菜单管理系统') # 创建界面控件 self.label1 = tk.Label(master, text='名称:') self.label1.grid(row=0, column=0) self.entry1 = tk.Entry(master) self.entry1.grid(row=0, column=1) self.label2 = tk.Label(master, text='价格:') self.label2.grid(row=1, column=0) self.entry2 = tk.Entry(master) self.entry2.grid(row=1, column=1) self.label3 = tk.Label(master, text='描述:') self.label3.grid(row=2, column=0) self.entry3 = tk.Entry(master) self.entry3.grid(row=2, column=1) self.button1 = tk.Button(master, text='添加', command=self.add_menu) self.button1.grid(row=3, column=0) self.button2 = tk.Button(master, text='删除', command=self.delete_menu) self.button2.grid(row=3, column=1) self.listbox = tk.Listbox(master) self.listbox.grid(row=4, column=0, columnspan=2) # 连接数据库 self.conn = sqlite3.connect('menu.db') self.c = self.conn.cursor() # 创建菜单表 self.c.execute('''CREATE TABLE IF NOT EXISTS menu (name TEXT, price REAL, description TEXT)''') # 显示菜单列表 self.show_menus() def add_menu(self): name = self.entry1.get() price = float(self.entry2.get()) description = self.entry3.get() # 插入菜单数据 self.c.execute("INSERT INTO menu VALUES (?, ?, ?)", (name, price, description)) self.conn.commit() # 清空界面控件 self.entry1.delete(0, 'end') self.entry2.delete(0, 'end') self.entry3.delete(0, 'end') # 刷新菜单列表 self.show_menus() def delete_menu(self): # 获取选中的菜单 selected = self.listbox.curselection() if not selected: return name = self.listbox.get(selected[0]) # 删除菜单数据 self.c.execute("DELETE FROM menu WHERE name=?", (name,)) self.conn.commit() # 刷新菜单列表 self.show_menus() def show_menus(self): # 清空菜单列表 self.listbox.delete(0, 'end') # 查询菜单数据 self.c.execute("SELECT name FROM menu") rows = self.c.fetchall() # 显示菜单列表 for row in rows: self.listbox.insert('end', row[0]) root = tk.Tk() app = MenuManager(root) root.mainloop() ``` 此示例中,我们使用了 Tkinter 库来创建界面控件,使用了 SQLite 数据库来存储菜单数据。在程序运行时,首先连接数据库并创建菜单表。然后,通过界面控件进行菜单数据的添加和删除,并将菜单列表显示在界面上。需要注意的是,添加和删除菜单数据时,需要先执行相应的 SQL 语句,然后通过 `commit()` 方法提交事务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值