修改badge

This commit is contained in:
sunjie
2025-09-02 21:55:12 +08:00
parent f4bc88e533
commit 45463bc28f
6 changed files with 166 additions and 107 deletions
@@ -3,7 +3,7 @@
<t t-name="AppIcon" owl="1">
<t t-set="app_id" t-value="app.id" />
<t t-set="menu_badge_count" t-value="this.getMenuBadgeCount(app)" />
<t t-set="menu_badge_count" t-value="app.badge_count" />
<a t-att-href="getMenuItemHref(app)" >
<t t-if="icon_style == 'standard'">
<img class="img" t-attf-src="/web/image?model=ir.ui.menu&amp;field=web_icon_data&amp;id={{app_id}}" t-att-title="app.name" t-att-alt="app.name" />
@@ -46,7 +46,8 @@
<t t-name="AppsTreeMenu" owl="1">
<!-- 子菜单 -->
<t t-set="children_menus" t-value="menu.childrenTree" />
<t t-set="menu_badge_count" t-value="this.getMenuBadgeCount(menu)" />
<!-- <t t-set="menu_badge_count" t-value="this.getMenuBadgeCount(menu)" />-->
<t t-set="menu_badge_count" t-value="menu.badge_count" />
<!-- 不存在子菜单 -->
<t t-if="!children_menus.length">
<div class="dropdown-header dropdown-item o_menu_header_lvl_1">
@@ -87,7 +88,8 @@
<ul class="dropdown-menu dropdown-menu-right" role="menu">
<t t-foreach="children_menus" t-as="children" t-key="children.id">
<t t-set="children_sub_menus" t-value="children.childrenTree" />
<t t-set="children_menu_badge_count" t-value="this.getMenuBadgeCount(children)" />
<t t-set="children_menu_badge_count" t-value="children.badge_count" />
<!-- <t t-set="children_menu_badge_count" t-value="this.getMenuBadgeCount(children)" />-->
<t t-if="children_sub_menus.length">
<t t-call="AppsTreeMenu">
<t t-set="menu" t-value="children" />
+8 -5
View File
@@ -12,12 +12,15 @@
"base",
"web",
],
"data": [],
"data": [
'views/webclient_templates.xml',
'views/ir_ui_menu.xml'
],
"assets": {
# "web.assets_backend": [
# "sun_badge_menu/static/src/webclient/navbar/navbar.js",
# "sun_badge_menu/static/src/webclient/navbar/navbar.xml"
# ],
"web.assets_backend": [
"sun_badge_menu/static/src/webclient/navbar/navbar.js",
"sun_badge_menu/static/src/webclient/navbar/navbar.xml"
],
},
"installable": True,
"application": True,
+76 -70
View File
@@ -3,7 +3,7 @@
import re
from odoo import models
from odoo import models, fields
def compute_badge_count_recursive(web_menus, menu_id):
@@ -29,6 +29,8 @@ def compute_badge_count_recursive(web_menus, menu_id):
class IrUiMenu(models.Model):
_inherit = "ir.ui.menu"
show_badge_count = fields.Boolean(string="显示菜单角标",default=True)
def load_web_menus(self, debug):
""" Loads all menu items (all applications and their sub-menus) and
processes them to be used by the webclient. Mainly, it associates with
@@ -38,72 +40,76 @@ class IrUiMenu(models.Model):
:return: the menus (including the images in Base64)
"""
return super(IrUiMenu, self).load_web_menus(debug)
# menus = self.load_menus(debug)
#
# web_menus = {}
# for menu in menus.values():
# if not menu['id']:
# # special root menu case
# web_menus['root'] = {
# "id": 'root',
# "name": menu['name'],
# "children": menu['children'],
# "appID": False,
# "xmlid": "",
# "actionID": False,
# "actionModel": False,
# "actionPath": False,
# "webIcon": None,
# "webIconData": None,
# "webIconDataMimetype": None,
# "backgroundImage": menu.get('backgroundImage'),
# }
# else:
# action = menu['action']
#
# if menu['id'] == menu['app_id']:
# # if it's an app take action of first (sub)child having one defined
# child = menu
# while child and not action:
# action = child['action']
# child = menus[child['children'][0]] if child['children'] else False
#
# action_model, action_id = action.split(',') if action else (False, False)
# action_id = int(action_id) if action_id else False
# badge_count = 0
# res_model = False
# if action_model and action_id:
# action_record = self.env[action_model].browse(action_id).sudo()
#
# if action_model == 'ir.actions.act_window' and action_record.view_mode != 'form':
# # 不仅仅是 form 表单,获取 model
# res_model = action_record.res_model
# # if res_model == 'sale.order':
# # print(self.env[action_record.res_model].get_menu_badge_count())
#
# if res_model in self.env and hasattr(self.env[res_model], 'get_menu_badge_count'):
# badge_count = self.env[res_model].get_menu_badge_count(action_record)
#
#
# web_menus[menu['id']] = {
# "id": menu['id'],
# "name": menu['name'],
# "children": menu['children'],
# "appID": menu['app_id'],
# "xmlid": menu['xmlid'],
# "actionID": action_id,
# "actionModel": action_model,
# "webIcon": menu['web_icon'],
# "webIconData": menu['web_icon_data'],
# "webIconDataMimetype": menu['web_icon_data_mimetype'],
# "badge_count": badge_count,
# "res_model": res_model
# }
#
# # 计算父级的菜单的徽章数
# for k, v in web_menus.items():
# if v['children']:
# web_menus[k]['badge_count'] = compute_badge_count_recursive(web_menus, k)
# # 对 root 节点重新计算
# return web_menus
menus = self.load_menus(debug)
web_menus = {}
for menu in menus.values():
if not menu['id']:
# special root menu case
web_menus['root'] = {
"id": 'root',
"name": menu['name'],
"children": menu['children'],
"appID": False,
"xmlid": "",
"actionID": False,
"actionModel": False,
"actionPath": False,
"webIcon": None,
"webIconData": None,
"webIconDataMimetype": None,
"backgroundImage": menu.get('backgroundImage')
}
else:
action = menu['action']
if menu['id'] == menu['app_id']:
# if it's an app take action of first (sub)child having one defined
child = menu
while child and not action:
action = child['action']
child = menus[child['children'][0]] if child['children'] else False
action_model, action_id = action.split(',') if action else (False, False)
action_id = int(action_id) if action_id else False
badge_count = 0
res_model = False
if action_model and action_id:
action_record = self.env[action_model].browse(action_id).sudo()
if action_model == 'ir.actions.act_window' and action_record.view_mode != 'form':
# 不仅仅是 form 表单,获取 model
res_model = action_record.res_model
if res_model in self.env:
if hasattr(self.env[res_model], 'get_menu_badge_count'):
badge_count = self.env[res_model].get_menu_badge_count(action_record)
elif hasattr(self.env[res_model], 'get_no_read_count'):
badge_count = self.env[res_model].get_no_read_count()
elif hasattr(res_model, 'read_number'):
badge_count = self.env[res_model].read_number()
elif hasattr(res_model, 'read_coming_count'):
badge_count = self.env[res_model].read_coming_count()
elif hasattr(res_model, 'read_due_count'):
badge_count = self.env[res_model].read_due_count()
web_menus[menu['id']] = {
"id": menu['id'],
"name": menu['name'],
"children": menu['children'],
"appID": menu['app_id'],
"xmlid": menu['xmlid'],
"actionID": action_id,
"actionModel": action_model,
"webIcon": menu['web_icon'],
"webIconData": menu['web_icon_data'],
"webIconDataMimetype": menu['web_icon_data_mimetype'],
"badge_count": badge_count,
"res_model": res_model
}
# 计算父级的菜单的徽章数
for k, v in web_menus.items():
if v['children']:
web_menus[k]['badge_count'] = compute_badge_count_recursive(web_menus, k)
# 对 root 节点重新计算
return web_menus
@@ -1,34 +1,37 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-inherit="web.NavBar.SectionsMenu.Dropdown.MenuSlot" t-inherit-mode="extension">
<xpath expr="//DropdownItem[@t-if='!item.childrenTree.length']" position="replace">
<DropdownItem
t-if="!item.childrenTree.length"
class="{
'dropdown-item': true,
o_dropdown_menu_group_entry: decalage gt 20
}"
onSelected="() => this.onNavBarDropdownItemSelection(item)"
attrs="{ style: style, href: getMenuItemHref(item), 'data-menu-xmlid': item.xmlid, 'data-section': item.id }"
>
<div class="d-flex justify-content-between">
<span t-esc="item.name"></span>
<span class="badge text-bg-danger rounded-pill" t-esc="item.badge_count"
t-if="item.badge_count > 0"></span>
</div>
</DropdownItem>
</xpath>
</t>
<!--<t t-inherit="web.NavBar.SectionsMenu.Dropdown.MenuSlot" t-inherit-mode="extension">-->
<!-- <xpath expr="//DropdownItem[@t-if='!item.childrenTree.length']" position="replace">-->
<!-- <DropdownItem-->
<!-- t-if="!item.childrenTree.length"-->
<!-- href="getMenuItemHref(item)"-->
<!-- class="{-->
<!-- 'dropdown-item': true,-->
<!-- o_dropdown_menu_group_entry: decalage gt 20-->
<!-- }"-->
<!-- style="style"-->
<!-- dataset="{ menuXmlid: item.xmlid, section: item.id }"-->
<!-- onSelected="() => this.onNavBarDropdownItemSelection(item)"-->
<!-- >-->
<!-- <div class="d-flex justify-content-between">-->
<!-- <span t-esc="item.name"></span>-->
<!-- <span class="badge text-bg-danger rounded-pill" t-esc="item.badge_count"-->
<!-- t-if="item.badge_count > 0"></span>-->
<!-- </div>-->
<!-- </DropdownItem>-->
<!-- </xpath>-->
<!--</t>-->
<t t-inherit="web.NavBar.SectionsMenu" t-inherit-mode="extension">
<xpath expr="//button[@t-att-data-hotkey='hotkey']" position="replace">
<button t-att-data-hotkey="hotkey" t-att-data-menu-xmlid="section.xmlid">
<span t-esc="section.name" t-att-data-section="section.id"/>
<span t-if="section.badge_count > 0" t-esc="section.badge_count" class="position-absolute z-1 start-100 translate-middle badge rounded-pill text-bg-danger">
<!--<t t-inherit="web.NavBar.SectionsMenu" t-inherit-mode="extension">-->
<!--<xpath expr="//button[@t-att-data-hotkey='hotkey']" position="replace">-->
<!-- <button t-att-data-hotkey="hotkey" t-att-data-menu-xmlid="section.xmlid">-->
<!-- <span t-esc="section.name" t-att-data-section="section.id"/>-->
<!-- <span t-if="section.badge_count > 0" t-esc="section.badge_count"-->
<!-- class="position-absolute z-1 start-100 translate-middle badge rounded-pill text-bg-danger">-->
</span>
</button>
</xpath>
</t>
</templates>
<!-- </span>-->
<!-- </button>-->
<!--</xpath>-->
<!--</t>-->
</templates>
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="edit_menu_access" model="ir.ui.view">
<field name="model">ir.ui.menu</field>
<field name="inherit_id" ref="base.edit_menu_access"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='sequence']" position="after">
<field name="show_badge_count"/>
</xpath>
</field>
</record>
</data>
</odoo>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<template id="webclient_bootstrap" inherit_id="web.webclient_bootstrap">
<xpath expr="//script" position="replace">
<script type="text/javascript">
// Block to avoid leaking variables in the script scope
{
odoo.__session_info__ = <t t-out="json.dumps(session_info)"/>;
const { user_context, cache_hashes } = odoo.__session_info__;
const lang = new URLSearchParams(document.location.search).get("lang");
let load_menus= new Date().getTime().toString();
let menuURL = `/web/webclient/load_menus/${load_menus}`;
if (lang) {
user_context.lang = lang;
menuURL += `?lang=${lang}`
}
odoo.reloadMenus = () => fetch(menuURL).then(res => res.json());
odoo.loadMenusPromise = odoo.reloadMenus();
// Prefetch translations to speedup webclient. This is done in JS because link rel="prefetch"
// is not yet supported on safari.
fetch(`/web/webclient/translations/${cache_hashes.translations}?lang=${user_context.lang}`);
}
</script>
</xpath>
</template>
</data>
</odoo>