Qtb #60
@@ -1,22 +1,48 @@
|
||||
/**
|
||||
* 隐藏「关于页」(yuthon.about.page) 表单右上角的「编辑」按钮。
|
||||
*
|
||||
* 原理:form 视图虽已写 edit="false",但教师/管理员组对该模型有写权限时,
|
||||
* Odoo 18 控制面板仍会渲染「编辑」按钮。CSS 选择器无法稳定命中控制面板
|
||||
* (它与 .o_form_view 是兄弟节点,且资产缓存/浏览器兼容性容易失效),
|
||||
* 因此通过 patch FormController,在当前模型为 yuthon.about.page 时,
|
||||
* 在 mounted/patched 生命周期里把控制面板中的编辑按钮隐藏掉。
|
||||
* 原理:form 视图已写 edit="false",Odoo 18 控制面板本就不该渲染编辑按钮;
|
||||
* 但为保险(防止某些角色/动作仍带出编辑入口),这里在 FormController 上 patch:
|
||||
* 当模型为 yuthon.about.page 时,用 MutationObserver 持续监听控制面板,
|
||||
* 一旦编辑按钮(无论何时、以何种形式出现)被渲染,立刻隐藏。
|
||||
*
|
||||
* 注意:本文件改动需要「升级模块」才会生效(-d <库名> -u college),
|
||||
* 并强刷浏览器 (Ctrl+Shift+R)。
|
||||
* 并强刷浏览器 (Ctrl+Shift+R)。仅 git pull + 重启不会重建前端资源包。
|
||||
*/
|
||||
/** @odoo-module **/
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { FormController } from "@web/views/form/form_controller";
|
||||
import { onMounted, onPatched } from "@odoo/owl";
|
||||
import { onMounted, onWillUnmount } from "@odoo/owl";
|
||||
|
||||
const ABOUT_MODEL = "yuthon.about.page";
|
||||
|
||||
// 判定一个按钮是否为「编辑」按钮(兼容不同渲染形态)
|
||||
function isEditButton(btn) {
|
||||
if (!btn || btn.tagName !== "BUTTON") {
|
||||
return false;
|
||||
}
|
||||
if (btn.classList.contains("o_form_button_edit")) {
|
||||
return true;
|
||||
}
|
||||
if (btn.getAttribute("data-hotkey") === "e") {
|
||||
return true;
|
||||
}
|
||||
const text = (btn.textContent || "").trim();
|
||||
if (text === "编辑" || text.toLowerCase() === "edit") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 在控制面板内隐藏所有编辑按钮
|
||||
function hideAboutEditButtons(root) {
|
||||
(root || document).querySelectorAll(".o_control_panel button").forEach((btn) => {
|
||||
if (isEditButton(btn)) {
|
||||
btn.style.display = "none";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
patch(FormController.prototype, {
|
||||
setup() {
|
||||
super.setup();
|
||||
@@ -24,23 +50,19 @@ patch(FormController.prototype, {
|
||||
return;
|
||||
}
|
||||
|
||||
const hideEditButton = () => {
|
||||
// Odoo 18 控制面板中「编辑」按钮的常见形态:
|
||||
// 1) class="o_form_button_edit"
|
||||
// 2) 快捷键 data-hotkey="e" 的主按钮
|
||||
const selectors = [
|
||||
".o_control_panel .o_form_button_edit",
|
||||
".o_control_panel button[data-hotkey='e']",
|
||||
];
|
||||
for (const selector of selectors) {
|
||||
document.querySelectorAll(selector).forEach((btn) => {
|
||||
btn.style.display = "none";
|
||||
});
|
||||
let observer = null;
|
||||
onMounted(() => {
|
||||
hideAboutEditButtons(document);
|
||||
// 控制面板按钮可能异步/重渲染后才出现,持续监听最稳妥
|
||||
const cp = document.querySelector(".o_control_panel");
|
||||
const target = cp || document.body;
|
||||
observer = new MutationObserver(() => hideAboutEditButtons(target));
|
||||
observer.observe(target, { childList: true, subtree: true });
|
||||
});
|
||||
onWillUnmount(() => {
|
||||
if (observer) {
|
||||
observer.disconnect();
|
||||
}
|
||||
};
|
||||
|
||||
// 首次渲染及后续 patch 都兜底隐藏,避免异步渲染导致按钮闪现
|
||||
onMounted(hideEditButton);
|
||||
onPatched(hideEditButton);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
侧边栏窄模式悬浮提示
|
||||
|
||||
背景:udoo_om_ux 左侧导航栏(.o_sidenav)拖窄 / 切换窄模式(宽度 < 142 时加 .sm)
|
||||
后,菜单文字 .nav-title 被 display:none 隐藏、只剩图标,而原生 title 属性恰好在
|
||||
被隐藏的 span 上,导致鼠标悬停看不到任何菜单名提示。
|
||||
|
||||
做法:继承 wub.NavBar.AppsMenu,给每个菜单外壳 div.nav-item 挂 Odoo 原生
|
||||
data-tooltip(黑色气泡,与系统其它提示一致),只在窄模式输出。
|
||||
|
||||
⚠ 为什么必须挂在外壳 div 上,而不是里面的 DropdownItem(踩过的坑):
|
||||
Odoo 在开发者模式(URL 带 ?debug=1,debug 值会存进 session)下,模板继承的
|
||||
applyInheritance() 会给每个被修改的元素「前面」插入一个调试注释节点
|
||||
(见 odoo/addons/web/static/src/core/template_inheritance.js:260)。
|
||||
- 如果改的是 .nav-item 的子元素,这个注释就成了 .nav-item 的 firstChild;
|
||||
而主题 OmuxNavBar.preSubnavTiming 里写死了
|
||||
evTarget.firstChild.classList.contains('on')
|
||||
注释节点没有 classList → 报
|
||||
TypeError: Cannot read properties of undefined (reading 'contains')
|
||||
- 改外壳后,注释落在 .sidenav_cont 里当兄弟节点:主题其它地方用的是
|
||||
element.children[...](只数元素,忽略注释),完全不受影响,悬停范围还是整行。
|
||||
|
||||
想改气泡方向把下面的 'right' 改成 'bottom' 即可。
|
||||
-->
|
||||
<templates xml:space="preserve">
|
||||
<t t-inherit="wub.NavBar.AppsMenu" t-inherit-mode="extension">
|
||||
<!-- 应用列表项(t-foreach 渲染出来的那一批) -->
|
||||
<xpath expr="//div[hasclass('nav-item')][@t-foreach]" position="attributes">
|
||||
<attribute name="t-att-data-tooltip">ue.sidenavWidth < 142 ? (app.label or app.name) : false</attribute>
|
||||
<attribute name="data-tooltip-position">right</attribute>
|
||||
</xpath>
|
||||
<!-- 收藏夹里「当前应用不在收藏中」时额外显示的那一项 -->
|
||||
<xpath expr="//div[hasclass('nav-item')][@t-if][not(@t-foreach)]" position="attributes">
|
||||
<attribute name="t-att-data-tooltip">ue.sidenavWidth < 142 ? (currentApp.label or currentApp.name) : false</attribute>
|
||||
<attribute name="data-tooltip-position">right</attribute>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -27,8 +27,8 @@ class LearningCourse(models.Model):
|
||||
syllabus_ids = fields.One2many('course.syllabus', 'course_id', string='教学大纲/教案')
|
||||
syllabus_count = fields.Integer(string='大纲/教案数', compute='_compute_syllabus_count', store=True)
|
||||
# 按类型统计
|
||||
syllabus_count_total = fields.Integer(string='大纲总数', compute='_compute_syllabus_count')
|
||||
lesson_plan_count = fields.Integer(string='教案总数', compute='_compute_syllabus_count')
|
||||
syllabus_count_total = fields.Integer(string='大纲总数', compute='_compute_syllabus_type_count')
|
||||
lesson_plan_count = fields.Integer(string='教案总数', compute='_compute_syllabus_type_count')
|
||||
# ==================== 统计资源(通过章节关联) ====================
|
||||
resource_count = fields.Integer(string='资源总数', compute='_compute_resource_count', store=True)
|
||||
video_count = fields.Integer(string='视频数', compute='_compute_resource_count', store=True)
|
||||
@@ -56,10 +56,14 @@ class LearningCourse(models.Model):
|
||||
for record in self:
|
||||
record.chapter_count = len(record.chapter_ids)
|
||||
|
||||
@api.depends('syllabus_ids', 'syllabus_ids.syllabus_type')
|
||||
@api.depends('syllabus_ids')
|
||||
def _compute_syllabus_count(self):
|
||||
for record in self:
|
||||
record.syllabus_count = len(record.syllabus_ids)
|
||||
|
||||
@api.depends('syllabus_ids', 'syllabus_ids.syllabus_type')
|
||||
def _compute_syllabus_type_count(self):
|
||||
for record in self:
|
||||
record.syllabus_count_total = len(record.syllabus_ids.filtered(lambda s: s.syllabus_type == 'syllabus'))
|
||||
record.lesson_plan_count = len(record.syllabus_ids.filtered(lambda s: s.syllabus_type == 'lesson_plan'))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user