From 81052e89d2b450bf252d9a3a2ec976d4400217ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Thu, 4 Jun 2026 22:57:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=A5=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_ai_agent/models/ai_report_template.py | 24 +++++++--- .../models/ai_report_template_config.py | 37 ++++++++++---- yuthon_ai_agent/views/ai_report_views.xml | 48 +++++++++---------- 3 files changed, 68 insertions(+), 41 deletions(-) diff --git a/yuthon_ai_agent/models/ai_report_template.py b/yuthon_ai_agent/models/ai_report_template.py index 1e4de2008..738e45c18 100644 --- a/yuthon_ai_agent/models/ai_report_template.py +++ b/yuthon_ai_agent/models/ai_report_template.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import json import logging +import re from odoo import models, fields, api from datetime import timedelta @@ -40,11 +41,13 @@ class AiReportTemplate(models.Model): string='列定义', copy=True) card_ids = fields.One2many('ai.report.template.card', 'template_id', string='汇总卡片', copy=True) - group_by_ids = fields.One2many('ai.report.template.group.by', 'template_id', + group_by_ids = fields.Many2many('ai.report.template.group.by', + 'ai_report_template_group_rel', 'template_id', 'group_by_id', string='分组设置', copy=True) color_rule_ids = fields.One2many('ai.report.template.color.rule', 'template_id', string='颜色规则', copy=True) - filter_ids = fields.One2many('ai.report.template.filter', 'template_id', + filter_ids = fields.Many2many('ai.report.template.filter', + 'ai_report_template_filter_rel', 'template_id', 'filter_id', string='筛选器', copy=True) param_def_ids = fields.One2many('ai.report.template.param.def', 'template_id', string='AI 参数定义', copy=True) @@ -60,8 +63,7 @@ class AiReportTemplate(models.Model): help='自动从参数定义行生成,也可手动编辑 JSON') # ===== Computed: report_config ===== - @api.depends('column_ids', 'column_ids.color_rule_ids', - 'card_ids', 'group_by_ids', + @api.depends('column_ids', 'card_ids', 'group_by_ids', 'filter_ids', 'default_visible') def _compute_report_config(self): for t in self: @@ -89,10 +91,20 @@ class AiReportTemplate(models.Model): cfg['groupBy'] = gb cr = [] for col in t.column_ids: - for r in col.color_rule_ids: + if col.color_value and col.cell_color: + # 解析操作符前缀: >= <= != > < = ~ + cv = col.color_value.strip() + m = re.match(r'^(>=|<=|!=|>|<|=|~)(.+)$', cv) + if m: + op_prefix, val = m.group(1), m.group(2) + op_map = {'=': 'eq', '~': 'contains', '!=': 'neq', + '>': '>', '<': '<', '>=': '>=', '<=': '<='} + op = op_map.get(op_prefix, 'eq') + else: + op, val = 'eq', cv cr.append({ 'field': col.field_name, - 'rules': [{'op': r.op, 'value': r.value, 'color': r.color}], + 'rules': [{'op': op, 'value': val.strip(), 'color': col.cell_color}], }) if cr: # 合并相同 field 的规则 diff --git a/yuthon_ai_agent/models/ai_report_template_config.py b/yuthon_ai_agent/models/ai_report_template_config.py index 2d9d0dbe7..39154e82c 100644 --- a/yuthon_ai_agent/models/ai_report_template_config.py +++ b/yuthon_ai_agent/models/ai_report_template_config.py @@ -2,7 +2,8 @@ import json import logging -from odoo import models, fields, api +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError _logger = logging.getLogger(__name__) @@ -23,9 +24,18 @@ class AiReportTemplateColumn(models.Model): label = fields.Char('显示名称', help='不填则默认使用字段名') width = fields.Integer('列宽', default=120) + color_value = fields.Char('匹配值', + help='格式:[操作符]值。不写操作符默认等于。\n' + ' =已完成 / 已完成 → 等于"已完成"\n' + ' >100 → 大于 100\n' + ' <50 → 小于 50\n' + ' >=100 → 大于等于 100\n' + ' <=50 → 小于等于 50\n' + ' !=已完成 → 不等于"已完成"\n' + ' ~张三 → 包含"张三"') + cell_color = fields.Char('显示颜色', default='#5DCAA5', + help='匹配成功时显示的颜色,如 #E74C3C(红)、#5DCAA5(绿)') sequence = fields.Integer('排序', default=10) - color_rule_ids = fields.One2many('ai.report.template.color.rule', 'column_id', - string='颜色规则') @api.onchange('field_id') def _onchange_field_id(self): @@ -34,6 +44,19 @@ class AiReportTemplateColumn(models.Model): if not self.label: self.label = self.field_id.field_description + @api.constrains('color_value') + def _check_color_value(self): + import re + for rec in self: + if not rec.color_value: + continue + # 合法格式: [>=|<=|!=|>|<|=|~]值 + if not re.match(r'^(>=|<=|!=|>|<|=|~)?\S', rec.color_value): + raise ValidationError( + '匹配值格式错误:%s\n' + '正确示例:已完成、>100、<50、>=100、<=50、!=已完成、~张三' % rec.color_value + ) + class AiReportTemplateCard(models.Model): """报表模板 - 汇总卡片""" @@ -72,9 +95,7 @@ class AiReportTemplateGroupBy(models.Model): _description = '分组设置' _order = 'sequence, id' - 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) + model_id = fields.Many2one('ir.model', string='数据模型') field_id = fields.Many2one('ir.model.fields', string='分组字段', domain="[('model_id', '=', model_id)]", help='选择分组字段,自动填充字段名和显示名称') @@ -127,9 +148,7 @@ class AiReportTemplateFilter(models.Model): _description = '筛选器' _order = 'sequence, id' - 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) + model_id = fields.Many2one('ir.model', string='数据模型') field_id = fields.Many2one('ir.model.fields', string='字段', domain="[('model_id', '=', model_id)]", help='选择筛选字段,自动填充字段名和显示名称') diff --git a/yuthon_ai_agent/views/ai_report_views.xml b/yuthon_ai_agent/views/ai_report_views.xml index c3fc2b0b5..f1a7bc50e 100644 --- a/yuthon_ai_agent/views/ai_report_views.xml +++ b/yuthon_ai_agent/views/ai_report_views.xml @@ -168,38 +168,18 @@ + domain="[('model_id','=',model_id)]" + context="{'default_model_id': model_id}" + placeholder="选择分组字段..."/> + domain="[('model_id','=',model_id)]" + context="{'default_model_id': model_id}" + placeholder="选择筛选器..."/> - - - - - - - - - - - - - - - - - - - - - @@ -216,6 +196,22 @@ + + + + + + + + + + + + + +