角标
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
"maintainer": "sun",
|
||||
"license" : "",
|
||||
"category": "Tools",
|
||||
"depends": ["base", "web",],
|
||||
"depends": ["base", "web"],
|
||||
"data": [
|
||||
'views/webclient_templates.xml',
|
||||
'views/ir_ui_menu.xml'
|
||||
|
||||
@@ -1,39 +1,46 @@
|
||||
/* sun_badge_menu — 菜单角标样式 */
|
||||
/* 角标由 sh_backmate_theme_adv_enterprise menu.xml 渲染 */
|
||||
/* 角标由 sh_backmate_theme_adv_enterprise menu.xml 渲染,此处覆盖为菜单项最右侧显示 */
|
||||
|
||||
/* 顶部应用菜单:<a> 标签是 badge 的定位参照 */
|
||||
.sh_backmate_theme_appmenu_div .o_app2,
|
||||
.sh_backmate_theme_appmenu_div .direct_menu,
|
||||
.sh_backmate_theme_appmenu_div a[href] {
|
||||
position: relative;
|
||||
/*
|
||||
* 主题 navbar.scss 中 .cssmenu > span > a > .menu-badge 的权重较高,
|
||||
* 普通 body.o_web_client .menu-badge 会被它覆盖。
|
||||
* 下面用重复类名提升选择器优先级,确保子菜单和顶级菜单的角标都定位到最右侧。
|
||||
*/
|
||||
|
||||
/* 给所有菜单链接容器加相对定位 */
|
||||
body.o_web_client .dropdown-item.dropdown-item,
|
||||
body.o_web_client .sh_dropdown_toggle.sh_dropdown_toggle,
|
||||
body.o_web_client .dropdown-header.dropdown-header,
|
||||
body.o_web_client .o_app.o_app a,
|
||||
body.o_web_client .o_app.o_app > a {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
/* 子菜单:.sh_submenu_icon_wrap 已有 position:relative(行内样式),无需额外设置 */
|
||||
|
||||
/* 角标基础样式 */
|
||||
.menu-badge {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -8px;
|
||||
min-width: 18px;
|
||||
height: 18px;
|
||||
padding: 0 5px;
|
||||
border-radius: 9px;
|
||||
background: #dc3545;
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
||||
z-index: 10;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 确保 badge 数字不换行 */
|
||||
.menu-badge {
|
||||
white-space: nowrap;
|
||||
/* 统一所有 .menu-badge 为菜单项最右侧、垂直居中 */
|
||||
body.o_web_client .menu-badge.menu-badge.menu-badge,
|
||||
body.o_web_client .cssmenu > span > a > .menu-badge.menu-badge,
|
||||
body.o_web_client .dropdown-item.dropdown-item > .menu-badge.menu-badge,
|
||||
body.o_web_client .sh_dropdown_toggle.sh_dropdown_toggle > .menu-badge.menu-badge,
|
||||
body.o_web_client .dropdown-header.dropdown-header .menu-badge.menu-badge,
|
||||
body.o_web_client .sh_backmate_theme_appmenu_div .o_app.o_app > span > a > .menu-badge.menu-badge {
|
||||
position: absolute !important;
|
||||
top: 50% !important;
|
||||
right: 12px !important;
|
||||
left: auto !important;
|
||||
bottom: auto !important;
|
||||
transform: translateY(-50%) !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
min-width: 18px !important;
|
||||
height: 18px !important;
|
||||
padding: 0 6px !important;
|
||||
margin: 0 !important;
|
||||
background-color: #dc3545 !important;
|
||||
color: #fff !important;
|
||||
font-size: 11px !important;
|
||||
font-weight: 700 !important;
|
||||
border-radius: 9px !important;
|
||||
line-height: 1 !important;
|
||||
vertical-align: middle !important;
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { patch } from "@web/core/utils/patch";
|
||||
import { deepCopy } from "@web/core/utils/objects";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { url } from "@web/core/utils/urls";
|
||||
import { onMounted, onWillUnmount } from "@odoo/owl";
|
||||
import { clickRefreshButton } from "../actions/client_actions";
|
||||
|
||||
const WKF_NOTIFY_TYPE = 'wkf_notify'
|
||||
@@ -67,5 +68,95 @@ patch(NavBar.prototype, {
|
||||
}
|
||||
}, 300)
|
||||
});
|
||||
|
||||
/**
|
||||
* 主题 navbar.scss 对子菜单的 .menu-badge 有更高权重定位,
|
||||
* 导致 CSS 覆盖不生效。此处用 MutationObserver 在菜单 DOM 变化后
|
||||
* 强制把 .menu-badge 设置为绝对定位到父链接最右侧。
|
||||
*/
|
||||
onMounted(() => {
|
||||
this._fixMenuBadgePosition();
|
||||
this.menuBadgeObserver = new MutationObserver(() => {
|
||||
this._fixMenuBadgePosition();
|
||||
});
|
||||
this.menuBadgeObserver.observe(document.body, { childList: true, subtree: true });
|
||||
});
|
||||
onWillUnmount(() => {
|
||||
if (this.menuBadgeObserver) {
|
||||
this.menuBadgeObserver.disconnect();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 修正所有菜单角标位置:推到菜单项最右侧。
|
||||
* 主题 XML 里角标放在图标容器(.sh_submenu_icon_wrap)内、文字前面,
|
||||
* 这里先用 JS 把角标真正移动到文字后面,再给菜单链接容器加 flex,
|
||||
* 最后用 margin-left:auto 把角标顶到最右。
|
||||
*/
|
||||
_fixMenuBadgePosition() {
|
||||
document.querySelectorAll('.menu-badge').forEach((badge) => {
|
||||
// 已处理过的直接跳过,避免 MutationObserver 循环
|
||||
if (badge.dataset.badgeMoved === '1') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 找到最近的菜单链接容器(<a> 或 .dropdown-item)
|
||||
let container = badge.parentElement;
|
||||
while (container && container.tagName !== 'A' && !container.classList.contains('dropdown-item') && !container.classList.contains('sh_dropdown_toggle')) {
|
||||
container = container.parentElement;
|
||||
if (!container || container.tagName === 'BODY') {
|
||||
container = badge.parentElement;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 把角标移动到文字 span 内部末尾,让它自然跟在文字后面
|
||||
let textSpan = null;
|
||||
const iconWrap = badge.closest('.sh_submenu_icon_wrap');
|
||||
if (iconWrap) {
|
||||
textSpan = iconWrap.nextElementSibling;
|
||||
} else if (badge.previousElementSibling && badge.previousElementSibling.tagName === 'IMG') {
|
||||
// 顶级 AppIcon:角标在 img 后面、文字前面
|
||||
textSpan = badge.nextElementSibling;
|
||||
} else {
|
||||
textSpan = badge.previousElementSibling;
|
||||
}
|
||||
if (textSpan && textSpan.tagName === 'SPAN') {
|
||||
textSpan.appendChild(badge);
|
||||
badge.dataset.badgeMoved = '1';
|
||||
}
|
||||
|
||||
// 还原容器 flex/width 等样式,让菜单项按原本块级布局
|
||||
container.style.display = '';
|
||||
container.style.flexDirection = '';
|
||||
container.style.alignItems = '';
|
||||
container.style.position = 'relative';
|
||||
container.style.width = '';
|
||||
container.style.justifyContent = '';
|
||||
|
||||
if (textSpan && textSpan.tagName === 'SPAN') {
|
||||
textSpan.style.setProperty('display', 'inline', 'important');
|
||||
textSpan.style.setProperty('flex', 'none', 'important');
|
||||
textSpan.style.setProperty('width', 'auto', 'important');
|
||||
}
|
||||
|
||||
// 角标作为文字 span 内的行内 flex 容器,紧跟文字并居中显示数字
|
||||
badge.style.setProperty('display', 'inline-flex', 'important');
|
||||
badge.style.setProperty('align-items', 'center', 'important');
|
||||
badge.style.setProperty('justify-content', 'center', 'important');
|
||||
badge.style.setProperty('position', 'static', 'important');
|
||||
badge.style.setProperty('top', 'auto', 'important');
|
||||
badge.style.setProperty('right', 'auto', 'important');
|
||||
badge.style.setProperty('left', 'auto', 'important');
|
||||
badge.style.setProperty('bottom', 'auto', 'important');
|
||||
badge.style.setProperty('transform', 'none', 'important');
|
||||
badge.style.setProperty('margin', '0 0 0 6px', 'important');
|
||||
badge.style.setProperty('vertical-align', 'middle', 'important');
|
||||
badge.style.setProperty('z-index', '1', 'important');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/* =========================================== */
|
||||
/* sun_badge_menu 角标样式:移到文字后面,加占位符 */
|
||||
/* =========================================== */
|
||||
|
||||
// 一级菜单行布局
|
||||
.app-icon-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.menu-text {
|
||||
flex-grow: 1;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
// 角标容器(固定宽度,保持对齐)
|
||||
.menu-badge-wrap {
|
||||
min-width: 22px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
// 角标数字(红色圆形)
|
||||
.badge-count {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: red;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
font-size: 11px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
// 占位符(无角标时预留空间,保持文字对齐)
|
||||
.badge-placeholder {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
Reference in New Issue
Block a user