注册数据

This commit is contained in:
李鹏宇
2026-07-28 19:06:32 +08:00
parent b0ec5c9ab8
commit ffd4bb8f08
17 changed files with 977 additions and 244 deletions
+8 -4
View File
@@ -1,13 +1,14 @@
{
'name': '宇森游戏基础数据',
'summary': '宇森游戏基础数据:图鉴 / 规则 / 论坛 / 公告',
'version': '1.2',
'summary': '宇森游戏基础数据:图鉴 / 规则 / 论坛 / 公告 / 玩家员工',
'version': '1.3',
'category': 'Game',
'sequence': 10,
'depends': ['base'],
'depends': ['base', 'hr'],
'data': [
'security/ir.model.access.csv',
'data/game_base_data.xml',
'data/game_achievement_data.xml',
'views/codex_views.xml',
'views/rule_category_views.xml',
'views/rule_views.xml',
@@ -15,10 +16,13 @@
'views/forum_views.xml',
'views/announcement_views.xml',
'views/story_views.xml',
'views/game_employee_views.xml',
'views/game_achievement_views.xml',
'views/game_mail_views.xml',
'views/menu.xml',
],
'application': True,
'license': 'LGPL-3',
'pre_init_hook': 'pre_init_hook',
'description': '基于官网内容沉淀的游戏基础数据模型,供后台维护、前端经 /game/api/* 读取;并提供与 Odoo 用户打通的登录/注册接口。',
'description': '基于官网内容沉淀的游戏基础数据模型,供后台维护、前端经 /game/api/* 读取;并提供与 Odoo 用户打通的登录/注册接口。注册时自动创建 hr.employee 并初始化游戏数据。',
}
+158 -4
View File
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
import json
from odoo import http
from odoo import http, fields
from odoo.http import request, Response
@@ -309,12 +309,52 @@ class GameApiController(http.Controller):
'groups_id': [(6, 0, [portal.id])],
})
request.env.cr.commit() # 让新用户立即对其他游标可见,便于随后自动登录
# === 自动创建 hr.employee 并初始化游戏数据 ===
Emp = request.env['hr.employee'].sudo()
emp = Emp.create({
'name': name or login,
'user_id': user.id,
'work_email': email or False,
'game_title': '萌新',
'game_level': 1,
'game_exp': 0,
'game_exp_max': 100,
'game_coins': 0,
'game_energy': 0,
'game_wood': 0,
'game_stone': 0,
'game_reg_date': fields.Date.today(),
})
# 生成游戏 UID(格式 10086 + 4位员工ID)
emp.game_uid = '10086%04d' % emp.id
# 给新玩家解锁第一个成就"开天辟地"
tmpl = request.env['game.achievement.template'].sudo().search(
[('code', '=', 'first_land')], limit=1)
if tmpl:
request.env['game.player.achievement'].sudo().create({
'employee_id': emp.id,
'template_id': tmpl.id,
'progress': tmpl.target,
'unlocked': True,
'unlock_date': fields.Datetime.now(),
})
# 给新玩家发一封欢迎邮件
request.env['game.player.mail'].sudo().create({
'employee_id': emp.id,
'category': 'system',
'subject': '欢迎来到宇森',
'sender': '系统',
'body': '<p>欢迎来到宇森!请查收新手礼包与世界探索指南。</p>',
})
request.env.cr.commit()
db = request.session.db or 'game'
auth_info = request.session.authenticate(db, {'type': 'password', 'login': login, 'password': password})
uid = auth_info['uid']
if uid:
return _json({'ok': True, 'uid': uid, 'name': user.name, 'login': user.login})
return _json({'ok': True, 'uid': user.id, 'name': user.name, 'login': user.login})
return _json({'ok': True, 'uid': uid, 'name': user.name, 'login': user.login,
'emp_id': emp.id, 'game_uid': emp.game_uid})
return _json({'ok': True, 'uid': user.id, 'name': user.name, 'login': user.login,
'emp_id': emp.id, 'game_uid': emp.game_uid})
except Exception as e:
return _json({'ok': False, 'error': '注册失败:%s' % str(e)})
@@ -324,9 +364,123 @@ class GameApiController(http.Controller):
if uid:
user = request.env['res.users'].sudo().browse(uid)
if user.exists():
return _json({'uid': uid, 'name': user.name, 'login': user.login})
data = {'uid': uid, 'name': user.name, 'login': user.login,
'email': user.email or ''}
emp = request.env['hr.employee'].sudo().search(
[('user_id', '=', uid)], limit=1)
if emp:
data.update(emp.export_game_data())
return _json(data)
return _json({'uid': None})
@http.route('/game/api/achievements', type='http', auth='public',
methods=['GET'], csrf=False)
def achievements(self, **kw):
"""返回当前登录玩家的成就列表(含模板+进度)。"""
uid = request.session.uid
if not uid:
return _json({'error': '未登录'})
emp = request.env['hr.employee'].sudo().search(
[('user_id', '=', uid)], limit=1)
if not emp:
return _json({'error': '玩家不存在', 'items': [], 'total_points': 0})
# 取所有启用的成就模板
tmpl_model = request.env['game.achievement.template'].sudo()
tier_labels = dict(tmpl_model._fields['tier'].selection)
templates = tmpl_model.search_read(
[('active', '=', True)],
['id', 'name', 'icon', 'tier', 'desc', 'points', 'target', 'sequence'],
order='sequence, id')
# 取该玩家已解锁的成就
player_recs = request.env['game.player.achievement'].sudo().search_read(
[('employee_id', '=', emp.id)],
['template_id', 'progress', 'unlocked', 'unlock_date'])
progress_map = {
r['template_id'][0]: r for r in player_recs
}
items = []
total_points = 0
unlocked_count = 0
for t in templates:
pr = progress_map.get(t['id'])
unlocked = pr and pr['unlocked']
progress = pr['progress'] if pr else 0
if unlocked:
total_points += t['points']
unlocked_count += 1
items.append({
'id': t['id'],
'name': t['name'],
'icon': t.get('icon') or '',
'tier': t['tier'],
'tier_label': tier_labels.get(t['tier'], t['tier']),
'desc': t.get('desc') or '',
'points': t['points'],
'target': t['target'],
'progress': progress,
'unlocked': bool(unlocked),
'unlock_date': str(pr['unlock_date']) if pr and pr['unlock_date'] else None,
})
return _json({
'items': items,
'total_points': total_points,
'unlocked_count': unlocked_count,
'total_count': len(items),
})
@http.route('/game/api/mails', type='http', auth='public',
methods=['GET'], csrf=False)
def mails(self, **kw):
"""返回当前登录玩家的邮件列表。"""
uid = request.session.uid
if not uid:
return _json({'error': '未登录', 'items': []})
emp = request.env['hr.employee'].sudo().search(
[('user_id', '=', uid)], limit=1)
if not emp:
return _json({'error': '玩家不存在', 'items': []})
cat_labels = dict(
request.env['game.player.mail'].sudo()._fields['category'].selection)
recs = request.env['game.player.mail'].sudo().search_read(
[('employee_id', '=', emp.id)],
['id', 'category', 'subject', 'body', 'date', 'is_read',
'is_pinned', 'sender'],
order='is_pinned desc, date desc')
items = [{
'id': r['id'],
'category': r['category'],
'category_label': cat_labels.get(r['category'], r['category']),
'subject': r['subject'],
'body': r.get('body') or '',
'date': str(r['date']) if r['date'] else '',
'is_read': r['is_read'],
'is_pinned': r['is_pinned'],
'sender': r.get('sender') or '',
} for r in recs]
unread = len([r for r in items if not r['is_read']])
return _json({'items': items, 'unread': unread})
@http.route('/game/api/mail/read', type='http', auth='public',
methods=['POST', 'OPTIONS'], csrf=False)
def mail_read(self, **kw):
"""标记邮件为已读。"""
uid = request.session.uid
if not uid:
return _json({'ok': False, 'error': '未登录'})
data = _post_body()
mail_id = data.get('mail_id')
if not mail_id:
return _json({'ok': False, 'error': '缺少 mail_id'})
emp = request.env['hr.employee'].sudo().search(
[('user_id', '=', uid)], limit=1)
if not emp:
return _json({'ok': False, 'error': '玩家不存在'})
mail = request.env['game.player.mail'].sudo().browse(mail_id)
if not mail.exists() or mail.employee_id != emp:
return _json({'ok': False, 'error': '邮件不存在或无权操作'})
mail.write({'is_read': True})
return _json({'ok': True})
@http.route('/game/api/logout', type='http', auth='public',
methods=['POST', 'OPTIONS'], csrf=False)
def logout(self, **kw):
@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- 成就模板初始数据 -->
<record model="game.achievement.template" id="ach_first_land">
<field name="code">first_land</field>
<field name="sequence">10</field>
<field name="name">开天辟地</field>
<field name="icon">star</field>
<field name="tier">gold</field>
<field name="points">50</field>
<field name="target">1</field>
<field name="desc">已点亮第一块领土,文明之火开始燃烧。</field>
</record>
<record model="game.achievement.template" id="ach_civilization_cradle">
<field name="code">civilization_cradle</field>
<field name="sequence">20</field>
<field name="name">文明摇篮</field>
<field name="icon">sprout</field>
<field name="tier">silver</field>
<field name="points">30</field>
<field name="target">5</field>
<field name="desc">建立 5 个聚落,文明初步成形。</field>
</record>
<record model="game.achievement.template" id="ach_sky_tower">
<field name="code">sky_tower</field>
<field name="sequence">30</field>
<field name="name">通天塔</field>
<field name="icon">lock</field>
<field name="tier">platinum</field>
<field name="points">100</field>
<field name="target">10</field>
<field name="desc">建造 10 座奇观,文明疆域遍布穹苍。</field>
</record>
<record model="game.achievement.template" id="ach_world_explorer">
<field name="code">world_explorer</field>
<field name="sequence">40</field>
<field name="name">世界探索者</field>
<field name="icon">compass</field>
<field name="tier">bronze</field>
<field name="points">10</field>
<field name="target">10</field>
<field name="desc">探索 10 块未知领土。</field>
</record>
<record model="game.achievement.template" id="ach_resource_master">
<field name="code">resource_master</field>
<field name="sequence">50</field>
<field name="name">资源大亨</field>
<field name="icon">gem</field>
<field name="tier">silver</field>
<field name="points">30</field>
<field name="target">1000</field>
<field name="desc">累计获得 1000 金币。</field>
</record>
<record model="game.achievement.template" id="ach_spirit_awakening">
<field name="code">spirit_awakening</field>
<field name="sequence">60</field>
<field name="name">灵气觉醒</field>
<field name="icon">sparkle</field>
<field name="tier">gold</field>
<field name="points">50</field>
<field name="target">1</field>
<field name="desc">世界阶段跃迁至「灵启」。</field>
</record>
<record model="game.achievement.template" id="ach_mystic_realm">
<field name="code">mystic_realm</field>
<field name="sequence">70</field>
<field name="name">玄界之主</field>
<field name="icon">crown</field>
<field name="tier">legend</field>
<field name="points">200</field>
<field name="target">1</field>
<field name="desc">世界阶段跃迁至「玄界」。</field>
</record>
<record model="game.achievement.template" id="ach_eternal">
<field name="code">eternal</field>
<field name="sequence">80</field>
<field name="name">永恒之锚</field>
<field name="icon">infinity</field>
<field name="tier">legend</field>
<field name="points">500</field>
<field name="target">1</field>
<field name="desc">世界阶段达到「溯元」,触及世界本源。</field>
</record>
</data>
</odoo>
+3
View File
@@ -6,3 +6,6 @@ from . import rule
from . import page
from . import story
from . import ir_http
from . import game_employee
from . import game_achievement
from . import game_mail
@@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models
class GameAchievementTemplate(models.Model):
"""全局成就模板:后台定义,所有玩家共用同一套成就定义。"""
_name = 'game.achievement.template'
_description = '成就模板'
_order = 'sequence, id'
_rec_name = 'name'
TIER = [
('bronze', '铜'),
('silver', '银'),
('gold', '金'),
('platinum', '铂金'),
('legend', '传奇'),
]
sequence = fields.Integer('排序', default=10, index=True)
code = fields.Char('标识', index=True,
help='唯一标识,用于代码引用,如 first_land')
name = fields.Char('成就名称', required=True, translate=True)
icon = fields.Char('图标', help='emoji 或符号,如 star / sprout / lock')
tier = fields.Selection(TIER, '品质', default='bronze', index=True)
desc = fields.Text('描述', help='解锁条件/说明')
target = fields.Integer('目标值', default=1,
help='解锁所需进度值,如 10 表示需完成 10 次')
points = fields.Integer('成就点数', default=10)
active = fields.Boolean('启用', default=True)
player_achievement_ids = fields.One2many(
'game.player.achievement', 'template_id', '玩家解锁记录')
class GamePlayerAchievement(models.Model):
"""玩家已解锁/进行中的成就,关联到 hr.employee。"""
_name = 'game.player.achievement'
_description = '玩家成就'
_order = 'template_id, id'
_rec_name = 'name'
template_id = fields.Many2one(
'game.achievement.template', '成就模板',
required=True, ondelete='cascade', index=True)
employee_id = fields.Many2one(
'hr.employee', '所属玩家',
required=True, ondelete='cascade', index=True)
name = fields.Char('名称', related='template_id.name', store=True)
icon = fields.Char('图标', related='template_id.icon')
tier = fields.Selection(
related='template_id.tier', store=True)
desc = fields.Text('描述', related='template_id.desc')
points = fields.Integer('点数', related='template_id.points')
progress = fields.Integer('进度', default=0)
target = fields.Integer('目标值', related='template_id.target')
unlocked = fields.Boolean('已解锁', default=False)
unlock_date = fields.Datetime('解锁时间')
@api.model
def _update_progress(self, employee_id, template_key, progress):
"""更新某玩家某成就的进度,达标自动解锁。"""
tmpl = self.env['game.achievement.template'].search(
[('id', '=', template_key)], limit=1)
if not tmpl:
return False
rec = self.search([
('employee_id', '=', employee_id),
('template_id', '=', tmpl.id),
], limit=1)
if not rec:
rec = self.create({
'employee_id': employee_id,
'template_id': tmpl.id,
'progress': min(progress, tmpl.target),
'unlocked': progress >= tmpl.target,
'unlock_date': fields.Datetime.now() if progress >= tmpl.target else False,
})
elif not rec.unlocked:
rec.write({
'progress': min(progress, tmpl.target),
'unlocked': progress >= tmpl.target,
})
if rec.unlocked:
rec.unlock_date = fields.Datetime.now()
return True
+74
View File
@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models
class GameEmployee(models.Model):
"""在 hr.employee 上扩展游戏数据字段。"""
_inherit = 'hr.employee'
# === 等级系统 ===
game_level = fields.Integer('游戏等级', default=1)
game_exp = fields.Integer('经验值', default=0)
game_exp_max = fields.Integer('升级经验', default=100)
game_title = fields.Char('称号', default='萌新')
# === 资源系统 ===
game_coins = fields.Integer('金币', default=0)
game_energy = fields.Integer('灵气', default=0)
game_wood = fields.Integer('木材', default=0)
game_stone = fields.Integer('石料', default=0)
# === 世界数据 ===
game_world_name = fields.Char('世界名')
game_era = fields.Char('历元', default='星元1年·春')
game_tiles = fields.Integer('疆域地块', default=0)
game_population = fields.Integer('人口', default=0)
game_civ_level = fields.Integer('文明度', default=0)
# === 身份 ===
game_uid = fields.Char('游戏UID', index=True)
game_reg_date = fields.Date('注册日期', default=fields.Date.today)
# === 关联 ===
achievement_ids = fields.One2many('game.player.achievement', 'employee_id', '成就')
mail_ids = fields.One2many('game.player.mail', 'employee_id', '邮件')
game_achievement_count = fields.Integer(
'已解锁成就数', compute='_compute_achievement_count')
game_unread_mail = fields.Integer(
'未读邮件数', compute='_compute_unread_mail')
@api.depends('achievement_ids.unlocked')
def _compute_achievement_count(self):
for rec in self:
rec.game_achievement_count = len(
rec.achievement_ids.filtered('unlocked'))
@api.depends('mail_ids.is_read')
def _compute_unread_mail(self):
for rec in self:
rec.game_unread_mail = len(
rec.mail_ids.filtered(lambda m: not m.is_read))
def export_game_data(self):
"""将游戏数据导出为前端可用的字典。"""
self.ensure_one()
return {
'emp_id': self.id,
'level': self.game_level,
'exp': self.game_exp,
'exp_max': self.game_exp_max,
'title': self.game_title,
'coins': self.game_coins,
'energy': self.game_energy,
'wood': self.game_wood,
'stone': self.game_stone,
'world_name': self.game_world_name or '',
'era': self.game_era or '',
'tiles': self.game_tiles,
'population': self.game_population,
'civ_level': self.game_civ_level,
'game_uid': self.game_uid or '',
'reg_date': str(self.game_reg_date) if self.game_reg_date else None,
'achievement_count': self.game_achievement_count,
'unread_mail': self.game_unread_mail,
}
+28
View File
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from odoo import api, fields, models
class GamePlayerMail(models.Model):
"""玩家邮件:后台可发送系统邮件给指定玩家(员工)。"""
_name = 'game.player.mail'
_description = '玩家邮件'
_order = 'date desc, id desc'
_rec_name = 'subject'
CATEGORY = [
('system', '系统'),
('activity', '活动'),
('reward', '奖励'),
('announcement', '公告'),
]
employee_id = fields.Many2one(
'hr.employee', '收件人',
required=True, ondelete='cascade', index=True)
category = fields.Selection(CATEGORY, '分类', default='system', index=True)
subject = fields.Char('标题', required=True)
body = fields.Html('正文')
date = fields.Datetime('发送时间', default=fields.Datetime.now)
is_read = fields.Boolean('已读', default=False)
is_pinned = fields.Boolean('置顶', default=False)
sender = fields.Char('发件人', default='系统')
@@ -19,3 +19,9 @@ access_game_story_volume,game.story.volume public,model_game_story_volume,base.g
access_game_story_volume_user,game.story.volume user,model_game_story_volume,base.group_user,1,1,1,1
access_game_story_chapter,game.story.chapter public,model_game_story_chapter,base.group_public,1,0,0,0
access_game_story_chapter_user,game.story.chapter user,model_game_story_chapter,base.group_user,1,1,1,1
access_game_achievement_template,game.achievement.template public,model_game_achievement_template,base.group_public,1,0,0,0
access_game_achievement_template_user,game.achievement.template user,model_game_achievement_template,base.group_user,1,1,1,1
access_game_player_achievement,game.player.achievement public,model_game_player_achievement,base.group_public,1,0,0,0
access_game_player_achievement_user,game.player.achievement user,model_game_player_achievement,base.group_user,1,1,1,1
access_game_player_mail,game.player.mail public,model_game_player_mail,base.group_public,1,0,0,0
access_game_player_mail_user,game.player.mail user,model_game_player_mail,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
19 access_game_story_volume_user game.story.volume user model_game_story_volume base.group_user 1 1 1 1
20 access_game_story_chapter game.story.chapter public model_game_story_chapter base.group_public 1 0 0 0
21 access_game_story_chapter_user game.story.chapter user model_game_story_chapter base.group_user 1 1 1 1
22 access_game_achievement_template game.achievement.template public model_game_achievement_template base.group_public 1 0 0 0
23 access_game_achievement_template_user game.achievement.template user model_game_achievement_template base.group_user 1 1 1 1
24 access_game_player_achievement game.player.achievement public model_game_player_achievement base.group_public 1 0 0 0
25 access_game_player_achievement_user game.player.achievement user model_game_player_achievement base.group_user 1 1 1 1
26 access_game_player_mail game.player.mail public model_game_player_mail base.group_public 1 0 0 0
27 access_game_player_mail_user game.player.mail user model_game_player_mail base.group_user 1 1 1 1
+1 -1
View File
@@ -28,7 +28,7 @@
</group>
<field name="body_html"/>
<field name="post_ids">
<tree><field name="author_name"/><field name="create_date"/></tree>
<list><field name="author_name"/><field name="create_date"/></list>
<form><field name="author_name"/><field name="body_html"/></form>
</field>
</form>
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- 成就模板列表 -->
<record model="ir.ui.view" id="view_game_achievement_template_tree">
<field name="name">game.achievement.template.tree</field>
<field name="model">game.achievement.template</field>
<field name="arch" type="xml">
<list>
<field name="sequence" widget="handle"/>
<field name="code"/>
<field name="name"/>
<field name="icon"/>
<field name="tier"/>
<field name="points"/>
<field name="target"/>
<field name="active" widget="boolean_toggle"/>
</list>
</field>
</record>
<!-- 成就模板表单 -->
<record model="ir.ui.view" id="view_game_achievement_template_form">
<field name="name">game.achievement.template.form</field>
<field name="model">game.achievement.template</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="code"/>
<field name="name"/>
<field name="icon" placeholder="emoji 或符号"/>
<field name="tier"/>
</group>
<group>
<field name="points"/>
<field name="target"/>
<field name="sequence"/>
<field name="active"/>
</group>
</group>
<group>
<field name="desc" placeholder="解锁条件/说明"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- 动作 -->
<record model="ir.actions.act_window" id="action_game_achievement_template">
<field name="name">成就模板</field>
<field name="res_model">game.achievement.template</field>
<field name="view_mode">tree,form</field>
</record>
<!-- 玩家成就列表(只读查看) -->
<record model="ir.ui.view" id="view_game_player_achievement_tree">
<field name="name">game.player.achievement.tree</field>
<field name="model">game.player.achievement</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
<field name="employee_id"/>
<field name="tier"/>
<field name="points"/>
<field name="progress"/>
<field name="unlocked"/>
<field name="unlock_date"/>
</list>
</field>
</record>
<record model="ir.actions.act_window" id="action_game_player_achievement">
<field name="name">玩家成就</field>
<field name="res_model">game.player.achievement</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- 继承 hr.employee 表单视图,添加「游戏数据」页签 -->
<record model="ir.ui.view" id="view_employee_game_form">
<field name="name">hr.employee.game.form</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page string="游戏数据" name="game_data">
<group>
<group string="等级系统">
<field name="game_uid"/>
<field name="game_level"/>
<field name="game_exp"/>
<field name="game_exp_max"/>
<field name="game_title"/>
<field name="game_reg_date"/>
</group>
<group string="资源">
<field name="game_coins"/>
<field name="game_energy"/>
<field name="game_wood"/>
<field name="game_stone"/>
</group>
</group>
<group>
<group string="世界数据">
<field name="game_world_name"/>
<field name="game_era"/>
<field name="game_tiles"/>
<field name="game_population"/>
<field name="game_civ_level"/>
</group>
<group string="统计">
<field name="game_achievement_count"/>
<field name="game_unread_mail"/>
</group>
</group>
</page>
</xpath>
</field>
</record>
<!-- 员工列表:增加游戏 UID/等级/称号 列 -->
<record model="ir.ui.view" id="view_employee_game_tree">
<field name="name">hr.employee.game.tree</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='name']" position="after">
<field name="game_uid" string="游戏UID"/>
<field name="game_level" string="等级"/>
<field name="game_title" string="称号"/>
</xpath>
</field>
</record>
</odoo>
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- 玩家邮件列表 -->
<record model="ir.ui.view" id="view_game_player_mail_tree">
<field name="name">game.player.mail.tree</field>
<field name="model">game.player.mail</field>
<field name="arch" type="xml">
<list>
<field name="is_pinned" widget="boolean_toggle"/>
<field name="subject"/>
<field name="employee_id" string="收件人"/>
<field name="category"/>
<field name="sender"/>
<field name="date"/>
<field name="is_read" widget="boolean_toggle"/>
</list>
</field>
</record>
<!-- 玩家邮件表单 -->
<record model="ir.ui.view" id="view_game_player_mail_form">
<field name="name">game.player.mail.form</field>
<field name="model">game.player.mail</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="employee_id" string="收件人"/>
<field name="category"/>
<field name="sender"/>
</group>
<group>
<field name="date"/>
<field name="is_pinned"/>
<field name="is_read"/>
</group>
</group>
<group>
<field name="subject"/>
<field name="body" widget="html"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- 动作 -->
<record model="ir.actions.act_window" id="action_game_player_mail">
<field name="name">玩家邮件</field>
<field name="res_model">game.player.mail</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>
+6
View File
@@ -26,6 +26,11 @@
<!-- 公告 -->
<menuitem id="menu_game_announcement" name="公告" parent="menu_game_root" action="action_game_announcement" sequence="40"/>
<!-- 玩家 -->
<menuitem id="menu_game_player" name="玩家" parent="menu_game_root" sequence="45"/>
<menuitem id="menu_game_player_achievement" name="玩家成就" parent="menu_game_player" action="action_game_player_achievement" sequence="46"/>
<menuitem id="menu_game_player_mail" name="玩家邮件" parent="menu_game_player" action="action_game_player_mail" sequence="47"/>
<!-- 小说(剧情) -->
<menuitem id="menu_game_story" name="小说" parent="menu_game_root" sequence="35"/>
<menuitem id="menu_game_story_volume" name="卷" parent="menu_game_story" action="action_game_story_volume" sequence="36"/>
@@ -37,5 +42,6 @@
<field name="action" eval="False"/>
</record>
<menuitem id="menu_game_rule_category" name="规则类别" parent="menu_game_config" action="action_game_rule_category" sequence="51"/>
<menuitem id="menu_game_achievement_template" name="成就模板" parent="menu_game_config" action="action_game_achievement_template" sequence="52"/>
</odoo>