19 Commits
Author SHA1 Message Date
qitangbo d73bd82769 完善教育 2026-09-23 15:33:10 +08:00
qitangbo 0d6d3e0bb9 右上角当前用户 2026-09-22 16:39:34 +08:00
qitangbo 84df154ce2 翻译 2026-09-21 19:40:38 +08:00
qitangbo ba32af9e04 课程searchpanel 2026-09-21 18:56:10 +08:00
qitangbo 07c29e451d 课程searchpanel 2026-09-21 18:55:27 +08:00
qitangbo 194594ffa5 Merge pull request '课程searchpanel' (#49) from qtb into master
Reviewed-on: #49
2026-09-21 18:42:26 +08:00
qitangbo 2b89ca6cb5 课程searchpanel 2026-09-21 18:26:02 +08:00
qitangbo 013aa37ebc Merge pull request '课程searchpanel' (#48) from qtb into master
Reviewed-on: #48
2026-09-21 18:23:06 +08:00
qitangbo 9f909e4833 课程searchpanel 2026-09-21 18:22:48 +08:00
qitangbo 544de24893 Merge pull request '添加应用类别配置和隐藏my_school' (#47) from qtb into master
Reviewed-on: #47
2026-09-21 17:52:04 +08:00
qitangbo 1b6ea810b2 feat(college): 应用类别配置菜单+中文列名、课程大纲字段、隐藏公司切换器 2026-09-21 17:49:11 +08:00
qitangbo f75aca9fd5 fix(learning_center): 清理 course_homework 残留的 Git 合并冲突标记 2026-09-21 17:49:10 +08:00
qitangbo aa07c87bbb Merge pull request '课程searchpanel' (#46) from qtb into master
Reviewed-on: #46
2026-09-18 17:36:39 +08:00
qitangbo 0bcbba4383 课程searchpanel 2026-09-18 17:35:34 +08:00
zhouxufan ad7bc5877d Merge pull request '完善教育' (#45) from zhou into master
Reviewed-on: #45
2026-09-18 17:23:12 +08:00
zhouxufan 2b186521ff Merge pull request '完善教育' (#44) from zhou into master
Reviewed-on: #44
2026-09-18 15:36:18 +08:00
zhouxufan 47dc1c428f Merge pull request '完善教育' (#43) from zhou into master
Reviewed-on: #43
2026-09-17 18:02:38 +08:00
zhouxufan f5967ae13d Merge pull request '完善教育' (#42) from zhou into master
Reviewed-on: #42
2026-09-17 17:23:19 +08:00
zhouxufan 40a9ed9bd1 Merge pull request '完善教育' (#41) from zhou into master
Reviewed-on: #41
2026-09-17 16:29:30 +08:00
13 changed files with 179 additions and 86 deletions
+3
View File
@@ -58,6 +58,9 @@
'assets': {
'web.assets_backend': [
'college/static/src/scss/college_theme.scss',
'college/static/src/js/hide_company_menu.js',
'college/static/src/js/portal_indicator.js',
'college/static/src/xml/portal_indicator.xml',
],
'web.assets_web_dark': [
'college/static/src/scss/college_theme_dark.scss',
-3
View File
@@ -84,7 +84,6 @@
<field name="end_date">2026-01-15</field>
<field name="introduction"><![CDATA[<p>面向零基础开发者,系统讲解 Odoo 18 的模块结构、模型(Model)、视图(View)与控制器(Controller),带你跑通第一个自定义模块。</p>]]></field>
<field name="objectives"><![CDATA[<ul><li>理解 Odoo 的 MVC 架构</li><li>能独立创建并安装一个模块</li><li>掌握模型字段与关系(M2O / O2M / M2M)</li></ul>]]></field>
<field name="syllabus"><![CDATA[<ol><li>开发环境搭建</li><li>模块清单与目录结构</li><li>模型定义与字段类型</li><li>视图(list / form / kanban)</li><li>菜单与权限(ACL)</li></ol>]]></field>
<field name="features"><![CDATA[<p>配套视频讲解 + 可运行示例代码,支持线下实训环境动手练习。</p>]]></field>
<field name="student_ids" eval="[(6, 0, [ref('student_wang'), ref('student_01'), ref('student_02'), ref('student_03')])]"/>
<field name="state">published</field>
@@ -102,7 +101,6 @@
<field name="end_date">2026-06-30</field>
<field name="introduction"><![CDATA[<p>深入 Odoo 的视图体系:列表、表单、看板、搜索、图形,以及 CSS 与 JS 资产定制。</p>]]></field>
<field name="objectives"><![CDATA[<ul><li>掌握 list/form/kanban 视图语法</li><li>能用 inherit 扩展已有视图</li><li>理解静态资源(assets)加载机制</li></ul>]]></field>
<field name="syllabus"><![CDATA[<ol><li>视图类型总览</li><li>视图继承与 xpath</li><li>看板与搜索视图</li><li>前端资产与主题定制</li></ol>]]></field>
<field name="features"><![CDATA[<p>视频 + PPT 双形态,含实操演练。</p>]]></field>
<field name="student_ids" eval="[(6, 0, [ref('student_02'), ref('student_04')])]"/>
<field name="state">draft</field>
@@ -120,7 +118,6 @@
<field name="end_date">2027-01-10</field>
<field name="introduction"><![CDATA[<p>以 PPT 讲义为主,讲解 QWeb 报表、PDF 打印与数据导出。</p>]]></field>
<field name="objectives"><![CDATA[<ul><li>掌握 QWeb 报表模板</li><li>能定制 PDF 打印</li></ul>]]></field>
<field name="syllabus"><![CDATA[<ol><li>报表模型与 action</li><li>QWeb 模板语法</li><li>打印与导出</li></ol>]]></field>
<field name="features"><![CDATA[<p>适合线下培训,讲师直接分发 PPT。</p>]]></field>
<field name="state">draft</field>
</record>
+60
View File
@@ -0,0 +1,60 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * college
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 18.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-21 11:31:00+0000\n"
"PO-Revision-Date: 2026-09-21 11:31:00+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=1; plural=0;\n"
"Language: zh_CN\n"
#. module: base
#: model:ir.module.category,name:base.module_category_education
msgid "Education"
msgstr "教育"
#. module: base
#: model:ir.module.category,name:base.module_category_game
msgid "Game"
msgstr "游戏"
#. module: base
#: model:ir.module.category,name:base.module_category_backend
msgid "Backend"
msgstr "后端"
#. module: base
#: model:ir.module.category,name:base.module_category_themes
msgid "Themes"
msgstr "主题"
# 以下四项 base 自带翻译已存在,这里补一份,
# 保证 college 模块单独升级时这几个类别也一定是中文。
#. module: base
#: model:ir.module.category,name:base.module_category_tools
#: model:ir.module.category,name:base.module_category_hidden_tools
msgid "Tools"
msgstr "工具"
#. module: base
#: model:ir.module.category,name:base.module_category_uncategorized
msgid "Uncategorized"
msgstr "未分类"
#. module: base
#: model:ir.module.category,name:base.module_category_localization
msgid "Localization"
msgstr "本地化"
#. module: base
#: model:ir.module.category,name:base.module_category_manufacturing_maintenance
msgid "Maintenance"
msgstr "维修"
+1
View File
@@ -75,6 +75,7 @@ class SchoolCourse(models.Model):
introduction = fields.Html(string='课程介绍', sanitize=True)
objectives = fields.Html(string='课程目标', sanitize=True)
features = fields.Html(string='课程特色', sanitize=True)
syllabus = fields.Html(string='课程大纲', sanitize=True)
business_scenario = fields.Html(string='业务情景', sanitize=True)
# 学员分配
@@ -0,0 +1,19 @@
/**
* 隐藏导航栏「公司切换器」(YourCompany 那一块)
*
* 原理:Odoo 导航栏右侧的系统托盘 (systray) 是一个组件注册表,
* web 模块在启动时把 SwitchCompanyMenu 注册了进去(见
* odoo18/addons/web/static/src/webclient/switch_company_menu/switch_company_menu.js
* 最后一行 registry.category("systray").add(...))。
* 这里在资源包加载阶段把它从注册表里摘掉,导航栏就不会渲染它。
*
* 单公司场景下该组件无意义;如需恢复,删除本文件并把
* __manifest__.py assets 里对应的一行去掉即可。
*
* 注意:本文件改动需要「升级模块 + 重启」才会生效
* (-d <库名> -u college 重启主进程),并强刷浏览器 (Ctrl+Shift+R)。
*/
/** @odoo-module **/
import { registry } from "@web/core/registry";
registry.category("systray").remove("SwitchCompanyMenu");
+31
View File
@@ -0,0 +1,31 @@
/** @odoo-module **/
import { registry } from "@web/core/registry";
import { user } from "@web/core/user";
import { Component, onWillStart, useState } from "@odoo/owl";
export class PortalIndicator extends Component {
static template = "college.PortalIndicator";
setup() {
this.state = useState({ label: "" });
onWillStart(async () => {
// 顺序:管理员 -> 老师 -> 学生(高权限优先)
if (await user.hasGroup("base.group_system")) {
this.state.label = "管理员端";
} else if (await user.hasGroup("college.group_teacher")) {
this.state.label = "教师端";
} else if (await user.hasGroup("college.group_student")) {
this.state.label = "学生端";
} else {
this.state.label = "用户端";
}
});
}
}
// sequence 越小越靠右(user_menu 是 0)。想贴最右边用负数。
registry.category("systray").add(
"college.PortalIndicator",
{ Component: PortalIndicator },
{ sequence: 1}
);
@@ -267,3 +267,26 @@
p, li { color: $o-main-text-color; }
}
// ============================================================
// 隐藏右上角「公司切换器」(我的学校)
// 项目为单学校场景,不需要用户切换公司。
// ============================================================
.o_switch_company_menu {
display: none !important;
}
// 角色提示徽章:避免被 Odoo 导航栏 .badge 通知样式影响
.o_college_portal_indicator {
align-self: center; // 在 systray 中垂直居中,不被拉伸/裁切
}
.o_portal_badge {
display: inline-block;
padding: 0.2em 0.7em;
font-size: 0.8rem;
line-height: 1.2;
color: black;
white-space: nowrap; // 不换行
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<div t-name="college.PortalIndicator"
class="o_college_portal_indicator d-flex align-items-center ms-2">
<span class="o_portal_badge" t-esc="state.label"/>
</div>
</templates>
+4 -1
View File
@@ -140,7 +140,6 @@
</group>
<notebook>
<page string="课程内容">
<field name="syllabus" readonly="1" string="课程大纲"/>
<separator string="课程章节(按顺序学习视频与资料)"/>
<field name="chapter_ids" readonly="1">
<list>
@@ -197,6 +196,10 @@
<filter name="group_state" string="状态"
context="{'group_by': 'state'}"/>
</group>
<searchpanel>
<field name="course_type" string="课程模块" enable_counters="1"/>
<field name="college" string="学院" enable_counters="1"/>
</searchpanel>
</search>
</field>
</record>
+28 -1
View File
@@ -388,8 +388,35 @@ else:
</record>
<menuitem id="menu_yuthon_about_question_import"
name="题库导入"
parent="menu_yuthon_exam_root"
parent="menu_yuthon_exam_question"
action="action_yuthon_about_question_import"
sequence="60"
groups="college.group_teacher,base.group_system"/>
<!-- ===================== 应用类别配置(挂在「实训模块」下) ===================== -->
<!-- Odoo 默认只有 form 视图,没有 list 视图,需要先补一个 -->
<record id="view_yuthon_module_category_list" model="ir.ui.view">
<field name="name">ir.module.category.list (college)</field>
<field name="model">ir.module.category</field>
<field name="arch" type="xml">
<list string="应用类别">
<field name="name" string="名称"/>
<field name="description" string="描述"/>
<field name="sequence" string="排序"/>
<field name="parent_id" string="上级应用"/>
</list>
</field>
</record>
<record id="action_yuthon_module_category" model="ir.actions.act_window">
<field name="name">应用类别配置</field>
<field name="res_model">ir.module.category</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="menu_yuthon_module_category"
name="应用类别配置"
parent="menu_shixun_module"
action="action_yuthon_module_category"
sequence="10"/>
</odoo>
+1 -71
View File
@@ -35,15 +35,7 @@ class CourseHomework(models.Model):
teaching_class_id = fields.Many2one('course.teaching_class', string='教学班',
ondelete='cascade', domain="[('course_id', '=', course_id)]")
submit_ids = fields.One2many('course.homework.submit', 'homework_id', string='提交记录')
<<<<<<< HEAD
<<<<<<< HEAD
score_item_ids = fields.One2many('course.score.item', 'homework_id', string='成绩项目')
=======
score_item_ids = fields.One2many('course.score.item', 'homework_id', string='成绩项')
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
=======
score_item_ids = fields.One2many('course.score.item', 'homework_id', string='成绩项')
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
# ====================== 新增:自动计算提交通道是否开启 ======================
@api.depends('state', 'deadline')
@@ -90,8 +82,6 @@ class CourseHomework(models.Model):
self.publish_time = fields.Datetime.now()
# 1. 批量为本班学生生成空白未提交记录
self._auto_create_empty_submit()
<<<<<<< HEAD
<<<<<<< HEAD
# 新增:自动生成对应作业的学生成绩明细
self._auto_create_homework_score_detail()
# 2. 推送作业通知给学生
@@ -147,15 +137,7 @@ class CourseHomework(models.Model):
if detail_vals_list:
self.env['course.student.score.detail'].create(detail_vals_list)
=======
# 2. 推送作业通知给学生
self._send_homework_notice_to_students()
=======
# 2. 推送作业通知给学生
self._send_homework_notice_to_students()
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
def _send_homework_notice_to_students(self):
"""发布作业后推送消息给教学班学生"""
self.ensure_one()
@@ -184,10 +166,6 @@ class CourseHomework(models.Model):
partner_ids=student_users.mapped('partner_id').ids,
subtype_xmlid='mail.mt_comment'
)
<<<<<<< HEAD
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
=======
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
def _auto_create_empty_submit(self):
"""仅发布时执行:自动给教学班所有已选课学生生成空白未提交记录"""
@@ -215,53 +193,12 @@ class CourseHomework(models.Model):
batch_vals.append({
'homework_id': self.id,
'student_id': stu_id,
<<<<<<< HEAD
<<<<<<< HEAD
'course_score_item_id':self.course_score_item_id.id,
=======
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
=======
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
'state': 'unsubmit' # 默认未提交
})
# 批量创建空白提交记录
submit_model.create(batch_vals)
<<<<<<< HEAD
<<<<<<< HEAD
def _send_homework_notice_to_students(self):
"""发布作业后推送消息给教学班学生"""
self.ensure_one()
if not self.teaching_class_id:
return
# 获取当前教学班所有已选课学生
enrolls = self.env['course.teaching_class.enrollment'].search([
('teaching_class_id', '=', self.teaching_class_id.id),
('state', '=', 'enrolled')
])
student_users = enrolls.mapped('student_id.user_id').filtered(lambda u: u)
if not student_users:
return
# 消息内容
subject = f"新作业发布:{self.name}"
body = f"""
<p>课程:{self.course_id.name}</p>
<p>作业名称:{self.name}</p>
<p>截止时间:{self.deadline or '无'}</p>
<p>作业要求:{self.requirement or '无'}</p>
"""
# 发送消息(关联当前作业记录,学生在消息中心可直接跳转)
self.message_post(
subject=subject,
body=body,
partner_ids=student_users.mapped('partner_id').ids,
subtype_xmlid='mail.mt_note'
)
=======
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
=======
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
# ====================== 草稿/关闭作业 ======================
def action_draft(self):
self.state = 'draft'
@@ -388,21 +325,14 @@ class CourseHomework(models.Model):
def create(self, vals):
homework_record = super().create(vals)
# 草稿状态不执行自动生成提交记录,移到action_publish
<<<<<<< HEAD
<<<<<<< HEAD
target_a = self.env["course.score.item"].search([
("name", "=", "作业"),
("create_uid", "=", self.env.uid)
], limit=1)
if target_a:
# 将刚新建的B追加到A的多对多字段
# 将新建的作业追加到"作业"成绩项目的多对多字段
target_a.write({
"homework_ids": [(4, homework_record.id)]
})
=======
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
=======
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
return homework_record
@@ -93,13 +93,6 @@ class CourseHomeworkSubmit(models.Model):
def create(self, vals):
submit_rec = super().create(vals)
self._check_submit_change_state(submit_rec)
<<<<<<< HEAD
<<<<<<< HEAD
=======
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
=======
>>>>>>> 0b2a587b69524dafde97f76bad27721afebdcddb
return submit_rec
def write(self, vals):
+2 -3
View File
@@ -1,7 +1,7 @@
{
'name': 'student_organization',
'name': '学生组织',
'version': '1.3',
'summary': '',
'summary': '管理学生组织架构、班级、年级等基础信息',
'sequence': 11,
'depends': ['base','hr'],
'data':[
@@ -14,6 +14,5 @@
'installable': True,
'application': True,
'license': 'LGPL-3',
}