模块迁移

This commit is contained in:
李鹏宇 2026-08-03 11:07:03 +08:00
parent 72e9b84d56
commit 6efe2130b8
13 changed files with 856 additions and 32 deletions

View File

@ -23,6 +23,9 @@
'views/progress_views.xml', 'views/progress_views.xml',
'views/elective_views.xml', 'views/elective_views.xml',
'views/progress_wizard_views.xml', 'views/progress_wizard_views.xml',
'views/course_views.xml',
'views/exam_views.xml',
'views/forum_post_views.xml',
'views/menus.xml', 'views/menus.xml',
], ],
'demo': [ 'demo': [

View File

@ -304,4 +304,207 @@
<field name="last_study_date">2026-07-28 08:00:00</field> <field name="last_study_date">2026-07-28 08:00:00</field>
</record> </record>
<!-- ============ 第二本教材 + 章节 ============ -->
<record id="demo_textbook_owl" model="yuthon.school.textbook">
<field name="name">Odoo 18 前端 OWL 实战</field>
<field name="category_id" ref="demo_category_view"/>
<field name="difficulty">intermediate</field>
<field name="state">published</field>
<field name="description">面向前端开发者的 OWL 组件化开发教程,覆盖组件、事件、状态与渲染。</field>
</record>
<record id="demo_chapter_owl_01" model="yuthon.school.chapter">
<field name="name">OWL 组件基础</field>
<field name="textbook_id" ref="demo_textbook_owl"/>
<field name="sequence">10</field>
<field name="hours">2.5</field>
<field name="summary">组件定义、props、状态</field>
<field name="key_points">Component、useState、props</field>
</record>
<record id="demo_chapter_owl_02" model="yuthon.school.chapter">
<field name="name">事件与渲染</field>
<field name="textbook_id" ref="demo_textbook_owl"/>
<field name="sequence">20</field>
<field name="hours">3.0</field>
<field name="summary">事件绑定、条件渲染、列表渲染</field>
<field name="key_points">t-on、t-if、t-foreach</field>
</record>
<!-- ============ 教学视频(占位文件,仅用于演示视频管理功能) ============ -->
<record id="demo_video_01" model="yuthon.school.video">
<field name="name">模块结构详解</field>
<field name="chapter_id" ref="demo_chapter_01"/>
<field name="category_id" ref="demo_category_basic"/>
<field name="duration">12:30</field>
<field name="difficulty">beginner</field>
<field name="video_filename">demo_lesson_01.mp4</field>
<field name="video_file" type="base64">AAAA</field>
<field name="description">讲解 Odoo 模块的目录结构与清单文件。</field>
<field name="key_points">__manifest__.py、data 加载顺序</field>
</record>
<record id="demo_video_02" model="yuthon.school.video">
<field name="name">模型与字段类型</field>
<field name="chapter_id" ref="demo_chapter_02"/>
<field name="category_id" ref="demo_category_model"/>
<field name="duration">18:05</field>
<field name="difficulty">intermediate</field>
<field name="video_filename">demo_lesson_02.mp4</field>
<field name="video_file" type="base64">AAAA</field>
<field name="description">Char/Integer/Selection 与关系字段。</field>
</record>
<record id="demo_video_03" model="yuthon.school.video">
<field name="name">OWL 组件基础</field>
<field name="chapter_id" ref="demo_chapter_owl_01"/>
<field name="category_id" ref="demo_category_view"/>
<field name="duration">15:40</field>
<field name="difficulty">intermediate</field>
<field name="video_filename">demo_lesson_03.mp4</field>
<field name="video_file" type="base64">AAAA</field>
<field name="description">OWL 组件定义与状态管理。</field>
</record>
<!-- ============ 课程 ============ -->
<record id="demo_course_fullstack" model="yuthon.school.course">
<field name="name">Odoo 18 全栈开发工程师</field>
<field name="code">ODOO-101</field>
<field name="category_id" ref="demo_category_root"/>
<field name="teacher_id" ref="demo_teacher_zhang"/>
<field name="difficulty">beginner</field>
<field name="state">ongoing</field>
<field name="description">零基础到独立开发 Odoo 18 模块,覆盖模型、视图、权限、控制器。</field>
<field name="textbook_ids" eval="[(6, 0, [ref('demo_textbook'), ref('demo_textbook_owl')])]"/>
<field name="student_ids" eval="[(6, 0, [ref('demo_student_01'), ref('demo_student_02'), ref('demo_student_03'), ref('demo_student_06')])]"/>
</record>
<record id="demo_course_frontend" model="yuthon.school.course">
<field name="name">Odoo 前端 OWL 专项</field>
<field name="code">ODOO-201</field>
<field name="category_id" ref="demo_category_view"/>
<field name="teacher_id" ref="demo_teacher_li"/>
<field name="difficulty">intermediate</field>
<field name="state">recruiting</field>
<field name="description">深入 OWL 框架,掌握组件化前端开发。</field>
<field name="textbook_ids" eval="[(6, 0, [ref('demo_textbook_owl')])]"/>
<field name="student_ids" eval="[(6, 0, [ref('demo_student_07'), ref('demo_student_08'), ref('demo_student_09')])]"/>
</record>
<!-- ============ 课程大纲 ============ -->
<record id="demo_syllabus_01" model="yuthon.school.syllabus">
<field name="name">模块开发与权限设计</field>
<field name="week">第 1-2 周</field>
<field name="course_id" ref="demo_course_fullstack"/>
<field name="chapter_ids" eval="[(6, 0, [ref('demo_chapter_01'), ref('demo_chapter_04')])]"/>
<field name="objective">掌握模块清单、模型定义与 ACL 权限机制。</field>
<field name="content"><![CDATA[<p>通过动手实现一个完整模块,理解 Odoo 的开发全流程。</p>]]></field>
</record>
<record id="demo_syllabus_02" model="yuthon.school.syllabus">
<field name="name">视图与前端交互</field>
<field name="week">第 3-4 周</field>
<field name="course_id" ref="demo_course_fullstack"/>
<field name="chapter_ids" eval="[(6, 0, [ref('demo_chapter_03')])]"/>
<field name="objective">熟练使用 list/form/search/kanban 四种视图。</field>
</record>
<record id="demo_syllabus_03" model="yuthon.school.syllabus">
<field name="name">OWL 组件与状态管理</field>
<field name="week">第 1-2 周</field>
<field name="course_id" ref="demo_course_frontend"/>
<field name="chapter_ids" eval="[(6, 0, [ref('demo_chapter_owl_01'), ref('demo_chapter_owl_02')])]"/>
<field name="objective">能用 OWL 编写组件化前端页面。</field>
</record>
<!-- ============ 训练考核 ============ -->
<record id="demo_exam_01" model="yuthon.school.exam">
<field name="name">模块开发单元测验</field>
<field name="course_id" ref="demo_course_fullstack"/>
<field name="student_id" ref="demo_student_01"/>
<field name="exam_type">quiz</field>
<field name="exam_date">2026-07-21</field>
<field name="score">92</field>
<field name="state">done</field>
<field name="note">概念清晰,注意 compute 字段的 store 用法。</field>
</record>
<record id="demo_exam_02" model="yuthon.school.exam">
<field name="name">模块开发单元测验</field>
<field name="course_id" ref="demo_course_fullstack"/>
<field name="student_id" ref="demo_student_02"/>
<field name="exam_type">quiz</field>
<field name="exam_date">2026-07-21</field>
<field name="score">55</field>
<field name="state">done</field>
<field name="note">关系字段部分需要加强。</field>
</record>
<record id="demo_exam_03" model="yuthon.school.exam">
<field name="name">OWL 期中练习</field>
<field name="course_id" ref="demo_course_frontend"/>
<field name="student_id" ref="demo_student_07"/>
<field name="exam_type">practice</field>
<field name="exam_date">2026-07-28</field>
<field name="score">88</field>
<field name="state">done</field>
</record>
<record id="demo_exam_04" model="yuthon.school.exam">
<field name="name">OWL 期中练习</field>
<field name="course_id" ref="demo_course_frontend"/>
<field name="student_id" ref="demo_student_08"/>
<field name="exam_type">practice</field>
<field name="exam_date">2026-07-28</field>
<field name="score">0</field>
<field name="state">pending</field>
</record>
<!-- ============ 学习交流 ============ -->
<record id="demo_post_01" model="yuthon.school.forum_post">
<field name="name">Many2many 的 (6,0,[ids]) 怎么理解?</field>
<field name="post_type">question</field>
<field name="author_name">陈小明</field>
<field name="author_type">student</field>
<field name="course_id" ref="demo_course_fullstack"/>
<field name="content"><![CDATA[<p>在 demo 数据里看到 <code>[(6,0,[ids])]</code>,这个 6 和 0 分别代表什么?</p>]]></field>
<field name="like_count">3</field>
</record>
<record id="demo_post_reply_01" model="yuthon.school.forum_post">
<field name="name">回复Many2many 的 (6,0,[ids])</field>
<field name="parent_id" ref="demo_post_01"/>
<field name="post_type">discuss</field>
<field name="author_name">张老师</field>
<field name="author_type">teacher</field>
<field name="content"><![CDATA[<p>6 表示「替换」0 是保留位,[ids] 是要关联的记录 id 列表。整体含义是把当前记录的 M2M 关系替换为给定列表。</p>]]></field>
<field name="like_count">5</field>
</record>
<record id="demo_post_02" model="yuthon.school.forum_post">
<field name="name">分享compute 字段一定要加 store 吗?</field>
<field name="post_type">share</field>
<field name="author_name">林小芳</field>
<field name="author_type">student</field>
<field name="course_id" ref="demo_course_fullstack"/>
<field name="content"><![CDATA[<p>不是必须。不加 store 每次读取都实时计算;加 store 会落库并随依赖字段变化重算,适合列表排序/搜索场景。</p>]]></field>
<field name="like_count">7</field>
</record>
<record id="demo_post_03" model="yuthon.school.forum_post">
<field name="name">OWL 里 t-on 和原生 onclick 有什么区别?</field>
<field name="post_type">question</field>
<field name="author_name">郑小伟</field>
<field name="author_type">student</field>
<field name="course_id" ref="demo_course_frontend"/>
<field name="content"><![CDATA[<p>用 t-on 绑定事件和直接写 onclick 有什么不同?</p>]]></field>
<field name="like_count">1</field>
</record>
</odoo> </odoo>

View File

@ -4,3 +4,7 @@ from . import textbook
from . import organization from . import organization
from . import progress from . import progress
from . import elective from . import elective
from . import course
from . import syllabus
from . import exam
from . import forum_post

View File

@ -0,0 +1,111 @@
from odoo import fields, models, api
class Course(models.Model):
_name = 'yuthon.school.course'
_description = '课程'
_order = 'sequence, name'
name = fields.Char(
string='课程名称',
required=True,
help='Odoo 18 全栈开发工程师认证课程',
)
code = fields.Char(
string='课程编号',
help='ODOO-101',
)
sequence = fields.Integer(
string='排序',
default=10,
)
category_id = fields.Many2one(
'yuthon.school.category',
string='学习分类',
ondelete='restrict',
)
teacher_id = fields.Many2one(
'yuthon.school.teacher',
string='授课教师',
)
difficulty = fields.Selection(
[('beginner', '入门'), ('intermediate', '进阶'), ('advanced', '高级')],
string='难度等级',
default='beginner',
)
state = fields.Selection(
[('draft', '草稿'), ('recruiting', '招生中'),
('ongoing', '进行中'), ('finished', '已结课')],
string='状态',
default='draft',
)
description = fields.Text(
string='课程简介',
help='课程目标、适合人群、考核方式等',
)
textbook_ids = fields.Many2many(
'yuthon.school.textbook',
'yuthon_course_textbook_rel',
'course_id', 'textbook_id',
string='关联教材',
help='本课程涵盖的教材Many2many 演示)',
)
syllabus_ids = fields.One2many(
'yuthon.school.syllabus',
'course_id',
string='课程大纲',
)
student_ids = fields.Many2many(
'yuthon.school.student',
'yuthon_course_student_rel',
'course_id', 'student_id',
string='选课学生',
help='选修本课程的学生Many2many 演示)',
)
total_hours = fields.Float(
string='总学时',
compute='_compute_total_hours',
store=True,
help='自动汇总关联教材各章学时',
)
enrollment_count = fields.Integer(
string='选课人数',
compute='_compute_counts',
store=True,
)
syllabus_count = fields.Integer(
string='大纲条目',
compute='_compute_counts',
store=True,
)
active = fields.Boolean(
string='启用',
default=True,
)
@api.depends('textbook_ids.chapter_ids.hours')
def _compute_total_hours(self):
for record in self:
record.total_hours = sum(
chapter.hours
for textbook in record.textbook_ids
for chapter in textbook.chapter_ids
)
@api.depends('student_ids', 'syllabus_ids')
def _compute_counts(self):
for record in self:
record.enrollment_count = len(record.student_ids)
record.syllabus_count = len(record.syllabus_ids)
def action_recruit(self):
"""开始招生"""
self.write({'state': 'recruiting'})
def action_start(self):
"""开课"""
self.write({'state': 'ongoing'})
def action_finish(self):
"""结课"""
self.write({'state': 'finished'})

View File

@ -0,0 +1,71 @@
from odoo import fields, models, api
class Exam(models.Model):
_name = 'yuthon.school.exam'
_description = '训练考核'
_order = 'exam_date desc, id'
name = fields.Char(
string='考核名称',
required=True,
help='Odoo 18 模块开发单元测验',
)
course_id = fields.Many2one(
'yuthon.school.course',
string='课程',
ondelete='restrict',
)
student_id = fields.Many2one(
'yuthon.school.student',
string='学生',
required=True,
ondelete='cascade',
)
exam_type = fields.Selection(
[('quiz', '测验'), ('practice', '练习'),
('midterm', '期中'), ('final', '期末')],
string='考核类型',
default='quiz',
)
exam_date = fields.Date(
string='考核日期',
)
score = fields.Float(
string='得分',
default=0.0,
)
total_score = fields.Float(
string='满分',
default=100.0,
)
pass_score = fields.Float(
string='及格线',
default=60.0,
)
state = fields.Selection(
[('pending', '待考'), ('done', '已考')],
string='状态',
default='pending',
)
is_pass = fields.Boolean(
string='是否及格',
compute='_compute_is_pass',
store=True,
)
note = fields.Text(
string='评语',
)
active = fields.Boolean(
string='启用',
default=True,
)
@api.depends('score', 'pass_score')
def _compute_is_pass(self):
for record in self:
record.is_pass = bool(record.score >= record.pass_score)
def action_mark_done(self):
"""标记已考"""
self.write({'state': 'done'})

View File

@ -0,0 +1,65 @@
from odoo import fields, models, api
class ForumPost(models.Model):
_name = 'yuthon.school.forum_post'
_description = '学习交流'
_order = 'create_date desc, id'
name = fields.Char(
string='主题',
required=True,
help='Many2many 的 (6,0,[ids]) 怎么理解?',
)
content = fields.Html(
string='内容',
sanitize=True,
)
author_name = fields.Char(
string='作者',
help='发帖人姓名(教学演示用,避免关联复杂权限)',
)
author_type = fields.Selection(
[('student', '学生'), ('teacher', '老师')],
string='作者类型',
default='student',
)
course_id = fields.Many2one(
'yuthon.school.course',
string='关联课程',
ondelete='restrict',
)
post_type = fields.Selection(
[('question', '提问'), ('share', '分享'), ('discuss', '讨论')],
string='类型',
default='question',
)
like_count = fields.Integer(
string='点赞数',
default=0,
)
parent_id = fields.Many2one(
'yuthon.school.forum_post',
string='回复对象',
ondelete='cascade',
help='非空表示这是某帖的回复(实现楼中楼)',
)
reply_ids = fields.One2many(
'yuthon.school.forum_post',
'parent_id',
string='回复',
)
reply_count = fields.Integer(
string='回复数',
compute='_compute_reply_count',
store=True,
)
active = fields.Boolean(
string='启用',
default=True,
)
@api.depends('reply_ids')
def _compute_reply_count(self):
for record in self:
record.reply_count = len(record.reply_ids)

View File

@ -0,0 +1,46 @@
from odoo import fields, models, api
class Syllabus(models.Model):
_name = 'yuthon.school.syllabus'
_description = '课程大纲'
_order = 'sequence, id'
name = fields.Char(
string='大纲条目',
required=True,
help='如:模块开发与权限设计',
)
sequence = fields.Integer(
string='排序',
default=10,
)
week = fields.Char(
string='周次',
help='如:第 1 周',
)
course_id = fields.Many2one(
'yuthon.school.course',
string='所属课程',
required=True,
ondelete='cascade',
)
chapter_ids = fields.Many2many(
'yuthon.school.chapter',
'yuthon_syllabus_chapter_rel',
'syllabus_id', 'chapter_id',
string='覆盖章节',
help='本条目对应的教材章节Many2many 演示)',
)
objective = fields.Text(
string='教学目标',
help='学完本条目应掌握的能力',
)
content = fields.Html(
string='大纲内容',
sanitize=True,
)
active = fields.Boolean(
string='启用',
default=True,
)

View File

@ -10,3 +10,7 @@ access_yuthon_school_student_user,yuthon.school.student.user,model_yuthon_school
access_yuthon_school_progress_user,yuthon.school.progress.user,model_yuthon_school_progress,base.group_user,1,1,1,1 access_yuthon_school_progress_user,yuthon.school.progress.user,model_yuthon_school_progress,base.group_user,1,1,1,1
access_yuthon_school_elective_user,yuthon.school.elective.user,model_yuthon_school_elective,base.group_user,1,1,1,1 access_yuthon_school_elective_user,yuthon.school.elective.user,model_yuthon_school_elective,base.group_user,1,1,1,1
access_yuthon_school_progress_wizard_user,yuthon.school.progress.wizard.user,model_yuthon_school_progress_wizard,base.group_user,1,1,1,1 access_yuthon_school_progress_wizard_user,yuthon.school.progress.wizard.user,model_yuthon_school_progress_wizard,base.group_user,1,1,1,1
access_yuthon_school_course_user,yuthon.school.course.user,model_yuthon_school_course,base.group_user,1,1,1,1
access_yuthon_school_syllabus_user,yuthon.school.syllabus.user,model_yuthon_school_syllabus,base.group_user,1,1,1,1
access_yuthon_school_exam_user,yuthon.school.exam.user,model_yuthon_school_exam,base.group_user,1,1,1,1
access_yuthon_school_forum_post_user,yuthon.school.forum_post.user,model_yuthon_school_forum_post,base.group_user,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
10 access_yuthon_school_progress_user yuthon.school.progress.user model_yuthon_school_progress base.group_user 1 1 1 1
11 access_yuthon_school_elective_user yuthon.school.elective.user model_yuthon_school_elective base.group_user 1 1 1 1
12 access_yuthon_school_progress_wizard_user yuthon.school.progress.wizard.user model_yuthon_school_progress_wizard base.group_user 1 1 1 1
13 access_yuthon_school_course_user yuthon.school.course.user model_yuthon_school_course base.group_user 1 1 1 1
14 access_yuthon_school_syllabus_user yuthon.school.syllabus.user model_yuthon_school_syllabus base.group_user 1 1 1 1
15 access_yuthon_school_exam_user yuthon.school.exam.user model_yuthon_school_exam base.group_user 1 1 1 1
16 access_yuthon_school_forum_post_user yuthon.school.forum_post.user model_yuthon_school_forum_post base.group_user 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,148 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- 课程:列表视图 -->
<record id="view_yuthon_school_course_list" model="ir.ui.view">
<field name="name">yuthon.school.course.list</field>
<field name="model">yuthon.school.course</field>
<field name="arch" type="xml">
<list string="课程">
<field name="name"/>
<field name="code"/>
<field name="category_id"/>
<field name="teacher_id"/>
<field name="difficulty"/>
<field name="state"/>
<field name="total_hours"/>
<field name="enrollment_count"/>
<field name="syllabus_count"/>
</list>
</field>
</record>
<!-- 课程:表单视图 -->
<record id="view_yuthon_school_course_form" model="ir.ui.view">
<field name="name">yuthon.school.course.form</field>
<field name="model">yuthon.school.course</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_recruit" type="object" string="开始招生"
class="btn-primary" invisible="state != 'draft'"/>
<button name="action_start" type="object" string="开课"
invisible="state not in ('draft', 'recruiting')"/>
<button name="action_finish" type="object" string="结课"
invisible="state != 'ongoing'"/>
<field name="state" widget="statusbar" statusbar_visible="draft,recruiting,ongoing,finished"/>
</header>
<sheet>
<group>
<group>
<field name="name"/>
<field name="code"/>
<field name="category_id"/>
<field name="teacher_id"/>
</group>
<group>
<field name="difficulty"/>
<field name="total_hours"/>
<field name="enrollment_count"/>
<field name="syllabus_count"/>
</group>
</group>
<field name="description" placeholder="课程目标、适合人群、考核方式……"/>
<notebook>
<page string="关联教材" name="textbooks">
<field name="textbook_ids" widget="many2many">
<tree>
<field name="name"/>
<field name="difficulty"/>
<field name="total_chapters"/>
<field name="total_hours"/>
<field name="total_videos"/>
<field name="state"/>
</tree>
<form>
<field name="name"/>
<field name="difficulty"/>
<field name="state"/>
<field name="description"/>
</form>
</field>
</page>
<page string="课程大纲" name="syllabus">
<field name="syllabus_ids">
<tree editable="bottom">
<field name="sequence" optional="show"/>
<field name="week"/>
<field name="name"/>
<field name="chapter_ids" widget="many2many_tags"/>
<field name="objective" optional="hide"/>
</tree>
<form>
<group>
<field name="name"/>
<field name="week"/>
<field name="course_id" invisible="1"/>
</group>
<group>
<field name="chapter_ids" widget="many2many_tags"
domain="[('textbook_id', 'in', parent.textbook_ids)]"/>
</group>
<field name="objective" placeholder="学完本条目应掌握的能力"/>
<field name="content"/>
</form>
</field>
</page>
<page string="选课学生" name="students">
<field name="student_ids" widget="many2many">
<tree>
<field name="name"/>
<field name="student_no"/>
<field name="class_id"/>
<field name="school_id"/>
</tree>
<form>
<field name="name"/>
<field name="student_no"/>
<field name="class_id"/>
</form>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<!-- 课程:搜索视图 -->
<record id="view_yuthon_school_course_search" model="ir.ui.view">
<field name="name">yuthon.school.course.search</field>
<field name="model">yuthon.school.course</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="code"/>
<field name="category_id"/>
<field name="teacher_id"/>
<field name="difficulty"/>
<field name="state"/>
</search>
</field>
</record>
<!-- 课程:动作 -->
<record id="action_yuthon_course" model="ir.actions.act_window">
<field name="name">课程管理</field>
<field name="res_model">yuthon.school.course</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_yuthon_school_course_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
暂无课程,点击「新建」创建一门课程(关联教材、大纲与选课学生)。
</p>
</field>
</record>
</odoo>

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- 训练考核:列表视图 -->
<record id="view_yuthon_school_exam_list" model="ir.ui.view">
<field name="name">yuthon.school.exam.list</field>
<field name="model">yuthon.school.exam</field>
<field name="arch" type="xml">
<list string="训练考核" decoration-success="is_pass" decoration-danger="is_pass == False and state == 'done'">
<field name="name"/>
<field name="course_id"/>
<field name="student_id"/>
<field name="exam_type"/>
<field name="exam_date"/>
<field name="score"/>
<field name="total_score"/>
<field name="is_pass" optional="show"/>
<field name="state"/>
</list>
</field>
</record>
<!-- 训练考核:表单视图 -->
<record id="view_yuthon_school_exam_form" model="ir.ui.view">
<field name="name">yuthon.school.exam.form</field>
<field name="model">yuthon.school.exam</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_mark_done" type="object" string="标记已考"
class="btn-primary" invisible="state != 'pending'"/>
<field name="state" widget="statusbar" statusbar_visible="pending,done"/>
</header>
<sheet>
<group>
<group>
<field name="name"/>
<field name="course_id"/>
<field name="student_id"/>
<field name="exam_type"/>
</group>
<group>
<field name="exam_date"/>
<field name="score"/>
<field name="total_score"/>
<field name="pass_score"/>
<field name="is_pass" readonly="1"/>
</group>
</group>
<field name="note" placeholder="评语……"/>
</sheet>
</form>
</field>
</record>
<!-- 训练考核:搜索视图 -->
<record id="view_yuthon_school_exam_search" model="ir.ui.view">
<field name="name">yuthon.school.exam.search</field>
<field name="model">yuthon.school.exam</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="course_id"/>
<field name="student_id"/>
<field name="exam_type"/>
<field name="state"/>
<filter string="已及格" name="passed" domain="[('is_pass', '=', True)]"/>
<filter string="未及格" name="failed" domain="[('is_pass', '=', False), ('state', '=', 'done')]"/>
</search>
</field>
</record>
<!-- 训练考核:动作 -->
<record id="action_yuthon_exam" model="ir.actions.act_window">
<field name="name">训练考核</field>
<field name="res_model">yuthon.school.exam</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_yuthon_school_exam_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
暂无考核记录,点击「新建」登记一次测验、练习或考试成绩。
</p>
</field>
</record>
</odoo>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- 学习交流:列表视图(仅显示主帖,回复在表单中展开) -->
<record id="view_yuthon_school_forum_post_list" model="ir.ui.view">
<field name="name">yuthon.school.forum_post.list</field>
<field name="model">yuthon.school.forum_post</field>
<field name="arch" type="xml">
<list string="学习交流" decoration-info="post_type == 'share'">
<field name="name"/>
<field name="post_type"/>
<field name="author_name"/>
<field name="author_type"/>
<field name="course_id"/>
<field name="like_count"/>
<field name="reply_count"/>
<field name="create_date"/>
</list>
</field>
</record>
<!-- 学习交流:表单视图 -->
<record id="view_yuthon_school_forum_post_form" model="ir.ui.view">
<field name="name">yuthon.school.forum_post.form</field>
<field name="model">yuthon.school.forum_post</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="name"/>
<field name="post_type"/>
<field name="course_id"/>
</group>
<group>
<field name="author_name"/>
<field name="author_type"/>
<field name="like_count"/>
</group>
</group>
<field name="content" placeholder="详细描述你的问题或分享……"/>
<separator string="回复"/>
<field name="reply_ids">
<tree>
<field name="name"/>
<field name="author_name"/>
<field name="author_type"/>
<field name="like_count"/>
<field name="create_date"/>
</tree>
<form>
<group>
<field name="name"/>
<field name="author_name"/>
<field name="author_type"/>
</group>
<field name="content"/>
</form>
</field>
</sheet>
</form>
</field>
</record>
<!-- 学习交流:搜索视图 -->
<record id="view_yuthon_school_forum_post_search" model="ir.ui.view">
<field name="name">yuthon.school.forum_post.search</field>
<field name="model">yuthon.school.forum_post</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<field name="course_id"/>
<field name="post_type"/>
<field name="author_type"/>
</search>
</field>
</record>
<!-- 学习交流:动作 -->
<record id="action_yuthon_forum_post" model="ir.actions.act_window">
<field name="name">学习交流</field>
<field name="res_model">yuthon.school.forum_post</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_yuthon_school_forum_post_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
还没有交流帖,点击「新建」发起一个提问、分享或讨论。
</p>
</field>
</record>
</odoo>

View File

@ -1,46 +1,41 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<!-- 说明:原「学习进度」菜单已移至「课程管理」下,作为「学习进展」 -->
<!-- Root menu --> <!-- ============ 课程管理 ============ -->
<menuitem id="menu_yuthon_school_root" <menuitem id="menu_yuthon_course_root"
name="教育" name="课程管理"
web_icon="yuthon_school,static/description/icon.png" web_icon="yuthon_school,static/description/icon.png"
sequence="10"/>
<!-- ============ 体系教材 ============ -->
<menuitem id="menu_yuthon_school_curriculum"
name="体系教材"
parent="menu_yuthon_school_root"
sequence="10"/>
<menuitem id="menu_yuthon_school_category"
name="学习分类"
parent="menu_yuthon_school_curriculum"
action="action_yuthon_school_category"
sequence="10"/>
<menuitem id="menu_yuthon_school_textbook"
name="教材管理"
parent="menu_yuthon_school_curriculum"
action="action_yuthon_school_textbook"
sequence="20"/>
<menuitem id="menu_yuthon_school_video"
name="视频管理"
parent="menu_yuthon_school_curriculum"
action="action_yuthon_school_video"
sequence="30"/> sequence="30"/>
<menuitem id="menu_yuthon_school_progress" <menuitem id="menu_yuthon_course"
name="学习进度" name="课程管理"
parent="menu_yuthon_school_curriculum" parent="menu_yuthon_course_root"
action="action_yuthon_course"
sequence="10"/>
<menuitem id="menu_yuthon_course_progress"
name="学习进展"
parent="menu_yuthon_course_root"
action="action_yuthon_school_progress" action="action_yuthon_school_progress"
sequence="20"/>
<menuitem id="menu_yuthon_exam"
name="训练考核"
parent="menu_yuthon_course_root"
action="action_yuthon_exam"
sequence="30"/>
<menuitem id="menu_yuthon_forum_post"
name="学习交流"
parent="menu_yuthon_course_root"
action="action_yuthon_forum_post"
sequence="40"/> sequence="40"/>
<!-- ============ 组织架构 ============ --> <!-- ============ 组织架构 ============ -->
<menuitem id="menu_yuthon_school_organization" <menuitem id="menu_yuthon_school_organization"
name="组织架构" name="组织架构"
parent="menu_yuthon_school_root" web_icon="yuthon_school,static/description/icon1.png"
sequence="20"/> sequence="20"/>
<menuitem id="menu_yuthon_school_school" <menuitem id="menu_yuthon_school_school"
@ -72,5 +67,4 @@
parent="menu_yuthon_school_organization" parent="menu_yuthon_school_organization"
action="action_yuthon_school_elective" action="action_yuthon_school_elective"
sequence="50"/> sequence="50"/>
</odoo> </odoo>