diff --git a/yuthon_ai_agent/models/ai_report_template_config.py b/yuthon_ai_agent/models/ai_report_template_config.py index cffa03850..16cb990be 100644 --- a/yuthon_ai_agent/models/ai_report_template_config.py +++ b/yuthon_ai_agent/models/ai_report_template_config.py @@ -15,12 +15,23 @@ class AiReportTemplateColumn(models.Model): template_id = fields.Many2one('ai.report.template', string='模板', required=True, ondelete='cascade') + model_id = fields.Many2one('ir.model', related='template_id.model_id', store=True) + field_id = fields.Many2one('ir.model.fields', string='字段', + domain="[('model_id', '=', model_id)]", + help='选择模型字段,自动填充字段名和显示名称') field_name = fields.Char('字段名', required=True) label = fields.Char('显示名称', help='不填则默认使用字段名') width = fields.Integer('列宽', default=120) sequence = fields.Integer('排序', default=10) + @api.onchange('field_id') + def _onchange_field_id(self): + if self.field_id: + self.field_name = self.field_id.name + if not self.label: + self.label = self.field_id.field_description + class AiReportTemplateCard(models.Model): """报表模板 - 汇总卡片""" @@ -30,8 +41,12 @@ class AiReportTemplateCard(models.Model): template_id = fields.Many2one('ai.report.template', string='模板', required=True, ondelete='cascade') + model_id = fields.Many2one('ir.model', related='template_id.model_id', store=True) + field_id = fields.Many2one('ir.model.fields', string='统计字段', + domain="[('model_id', '=', model_id), ('ttype', 'in', ('integer', 'float', 'monetary'))]", + help='选择数值型字段,自动填充字段名') + field_name = fields.Char('统计字段名', required=True) label = fields.Char('卡片标签', required=True) - field_name = fields.Char('统计字段', required=True) calc = fields.Selection([ ('sum', '求和'), ('count', '计数'), @@ -41,6 +56,11 @@ class AiReportTemplateCard(models.Model): unit = fields.Char('单位', help='如 万、% 等') sequence = fields.Integer('排序', default=10) + @api.onchange('field_id') + def _onchange_field_id(self): + if self.field_id: + self.field_name = self.field_id.name + class AiReportTemplateGroupBy(models.Model): """报表模板 - 分组设置""" @@ -50,10 +70,21 @@ class AiReportTemplateGroupBy(models.Model): template_id = fields.Many2one('ai.report.template', string='模板', required=True, ondelete='cascade') - field_name = fields.Char('分组字段', required=True) + model_id = fields.Many2one('ir.model', related='template_id.model_id', store=True) + field_id = fields.Many2one('ir.model.fields', string='分组字段', + domain="[('model_id', '=', model_id)]", + help='选择分组字段,自动填充字段名和显示名称') + field_name = fields.Char('分组字段名', required=True) label = fields.Char('显示名称') sequence = fields.Integer('排序', default=10) + @api.onchange('field_id') + def _onchange_field_id(self): + if self.field_id: + self.field_name = self.field_id.name + if not self.label: + self.label = self.field_id.field_description + class AiReportTemplateColorRule(models.Model): """报表模板 - 颜色规则""" @@ -63,6 +94,10 @@ class AiReportTemplateColorRule(models.Model): template_id = fields.Many2one('ai.report.template', string='模板', required=True, ondelete='cascade') + model_id = fields.Many2one('ir.model', related='template_id.model_id', store=True) + field_id = fields.Many2one('ir.model.fields', string='字段', + domain="[('model_id', '=', model_id)]", + help='选择要应用颜色规则的字段') field_name = fields.Char('字段名', required=True) op = fields.Selection([ ('eq', '等于'), @@ -71,12 +106,19 @@ class AiReportTemplateColorRule(models.Model): ('>', '大于'), ('<', '小于'), ('contains', '包含'), + ('neq', '不等于'), ], string='操作符', default='eq') - value = fields.Char('值') + value = fields.Char('匹配值', required=True, + help='匹配的值,如"已完成"或"100"') color = fields.Char('颜色', default='#5DCAA5', - help='十六进制颜色代码') + help='十六进制颜色代码,如 #E74C3C(红)、#5DCAA5(绿)、#F39C12(橙)') sequence = fields.Integer('排序', default=10) + @api.onchange('field_id') + def _onchange_field_id(self): + if self.field_id: + self.field_name = self.field_id.name + class AiReportTemplateFilter(models.Model): """报表模板 - 筛选器""" @@ -86,6 +128,10 @@ class AiReportTemplateFilter(models.Model): template_id = fields.Many2one('ai.report.template', string='模板', required=True, ondelete='cascade') + model_id = fields.Many2one('ir.model', related='template_id.model_id', store=True) + field_id = fields.Many2one('ir.model.fields', string='字段', + domain="[('model_id', '=', model_id)]", + help='选择筛选字段,自动填充字段名和显示名称') field_name = fields.Char('字段名', required=True) filter_type = fields.Selection([ ('input', '输入框'), @@ -96,6 +142,13 @@ class AiReportTemplateFilter(models.Model): help='下拉选择时,逗号分隔的选项列表。如:草稿,已确认,已完成') sequence = fields.Integer('排序', default=10) + @api.onchange('field_id') + def _onchange_field_id(self): + if self.field_id: + self.field_name = self.field_id.name + if not self.label: + self.label = self.field_id.field_description + class AiReportTemplateParamDef(models.Model): """报表模板 - AI参数定义""" @@ -105,6 +158,7 @@ class AiReportTemplateParamDef(models.Model): template_id = fields.Many2one('ai.report.template', string='模板', required=True, ondelete='cascade') + model_id = fields.Many2one('ir.model', related='template_id.model_id', store=True) name = fields.Char('参数名', required=True, help='AI 识别参数用的键名,如 date_from') label = fields.Char('显示名', @@ -116,7 +170,10 @@ class AiReportTemplateParamDef(models.Model): ('float', '数字'), ('int', '整数'), ], string='参数类型', default='char') - domain_field = fields.Char('映射字段', + field_id = fields.Many2one('ir.model.fields', string='映射字段', + domain="[('model_id', '=', model_id)]", + help='选择要映射的模型字段,自动填充映射字段名') + domain_field = fields.Char('映射字段名', help='映射到模型字段名,如 date_order') op = fields.Char('操作符', default='=', help='Odoo domain 操作符,如 >=、<=、ilike、=') @@ -126,3 +183,8 @@ class AiReportTemplateParamDef(models.Model): help='select 类型时,逗号分隔的选项') required = fields.Boolean('必填') sequence = fields.Integer('排序', default=10) + + @api.onchange('field_id') + def _onchange_field_id(self): + if self.field_id: + self.domain_field = self.field_id.name diff --git a/yuthon_ai_agent/static/src/html/report_template.html b/yuthon_ai_agent/static/src/html/report_template.html index 413f885bc..51c3c2191 100644 --- a/yuthon_ai_agent/static/src/html/report_template.html +++ b/yuthon_ai_agent/static/src/html/report_template.html @@ -294,11 +294,12 @@ function buildFilters(){ const filters=REPORT_CONFIG.filters||[]; let html='
'; filters.forEach((f,i)=>{ - if(f.type==='select'||f.filter_type==='select'){ - const opts=(f.options||'').split(',').map(o=>o.trim()).filter(Boolean); - html+=``; + const lbl=f.label||COLS.find(c=>c.k===f.field)?.l||f.field||''; + if(f.type==='select'){ + const opts=Array.isArray(f.options)?f.options:(f.options||'').split(',').map(o=>o.trim()).filter(Boolean); + html+=``; }else{ - html+=``; + html+=``; } }); if(filters.length>0){ @@ -311,15 +312,13 @@ function flt(){ const g=$('fG')?$('fG').value.trim().toLowerCase():''; const filters=REPORT_CONFIG.filters||[]; return data.filter(r=>{ - // 动态筛选器 for(let i=0;i