diff --git a/yuthon_ai_agent/data/demo_report_template.xml b/yuthon_ai_agent/data/demo_report_template.xml
index 14f3430da..e22bdc95f 100644
--- a/yuthon_ai_agent/data/demo_report_template.xml
+++ b/yuthon_ai_agent/data/demo_report_template.xml
@@ -12,7 +12,7 @@
{"name":"订单号","partner_id":"客户","date_order":"下单日期","amount_total":"金额","state":"状态","user_id":"销售员"}
date_order desc
5
-
+
=","description":"查询起始日期"},{"name":"date_to","label":"结束日期","type":"date","domain_field":"date_order","op":"<=","description":"查询截止日期"}]]]>
当用户查询销售订单列表或销售业绩时使用此模板。支持按日期范围筛选。
10
@@ -26,7 +26,7 @@
{"name":"线索名称","partner_name":"客户","expected_revenue":"预期收入","probability":"赢单率","stage_id":"阶段","create_date":"创建时间","user_id":"负责人"}
create_date desc
6
-
+
当用户查询销售线索列表或销售管道时使用此模板。
20
diff --git a/yuthon_ai_agent/models/ai_report_template.py b/yuthon_ai_agent/models/ai_report_template.py
index e157524e1..cae02896b 100644
--- a/yuthon_ai_agent/models/ai_report_template.py
+++ b/yuthon_ai_agent/models/ai_report_template.py
@@ -69,6 +69,8 @@ class AiReportTemplate(models.Model):
help='列定义中已配置的字段,用于限制分组/筛选器的可选范围')
param_def_ids = fields.One2many('ai.report.template.param.def', 'template_id',
string='AI 参数定义', copy=True)
+ chart_ids = fields.One2many('ai.report.template.chart', 'template_id',
+ string='图表配置', copy=True)
default_visible = fields.Integer('默认可见列数', default=10)
@@ -88,7 +90,7 @@ class AiReportTemplate(models.Model):
# ===== Computed: report_config =====
@api.depends('column_ids', 'card_ids', 'group_by_ids',
- 'filter_ids', 'default_visible')
+ 'filter_ids', 'default_visible', 'chart_ids')
def _compute_report_config(self):
for t in self:
cfg = {}
@@ -162,6 +164,23 @@ class AiReportTemplate(models.Model):
cfg['filters'] = flt
if t.default_visible:
cfg['defaultVisible'] = t.default_visible
+ charts = []
+ for ch in t.chart_ids:
+ lf_name = ch.label_field_name or (ch.label_field_id.name if ch.label_field_id else '')
+ vf_name = ch.value_field_name or (ch.value_field_id.name if ch.value_field_id else '')
+ if not lf_name:
+ continue
+ cd = {
+ 'type': ch.chart_type,
+ 'labelField': lf_name,
+ 'calc': ch.calc_method,
+ 'title': ch.title or '',
+ }
+ if vf_name:
+ cd['valueField'] = vf_name
+ charts.append(cd)
+ if charts:
+ cfg['charts'] = charts
t.report_config = json.dumps(cfg, ensure_ascii=False) if cfg else '{}'
def _inverse_report_config(self):
diff --git a/yuthon_ai_agent/models/ai_report_template_config.py b/yuthon_ai_agent/models/ai_report_template_config.py
index cdd6ceb10..105a2cac8 100644
--- a/yuthon_ai_agent/models/ai_report_template_config.py
+++ b/yuthon_ai_agent/models/ai_report_template_config.py
@@ -200,6 +200,51 @@ class AiReportTemplateParamDef(models.Model):
self.label = self.field_id.field_description
+class AiReportTemplateChart(models.Model):
+ """报表模板 - 图表配置"""
+ _name = 'ai.report.template.chart'
+ _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)
+ chart_type = fields.Selection([
+ ('bar', '柱状图'),
+ ('line', '折线图'),
+ ('pie', '饼图'),
+ ('doughnut', '环形图'),
+ ('polarArea', '玫瑰图'),
+ ], string='图表类型', default='bar', required=True)
+ label_field_id = fields.Many2one('ir.model.fields', string='分组字段',
+ domain="[('model_id', '=', model_id)]", required=True,
+ help='作为图表分类轴的字段')
+ label_field_name = fields.Char('分组字段名',
+ compute='_compute_label_field_name', store=True)
+ value_field_id = fields.Many2one('ir.model.fields', string='数值字段',
+ domain="[('model_id', '=', model_id), ('ttype', 'in', ('integer', 'float', 'monetary'))]",
+ help='留空则使用计数')
+ value_field_name = fields.Char('数值字段名',
+ compute='_compute_value_field_name', store=True)
+ calc_method = fields.Selection([
+ ('count', '计数'),
+ ('sum', '求和'),
+ ('avg', '平均值'),
+ ], string='计算方式', default='count')
+ title = fields.Char('图表标题')
+ sequence = fields.Integer('排序', default=10)
+
+ @api.depends('label_field_id')
+ def _compute_label_field_name(self):
+ for r in self:
+ r.label_field_name = r.label_field_id.name if r.label_field_id else ''
+
+ @api.depends('value_field_id')
+ def _compute_value_field_name(self):
+ for r in self:
+ r.value_field_name = r.value_field_id.name if r.value_field_id else ''
+
+
class AiReportTemplateDomainLine(models.Model):
_name = 'ai.report.template.domain.line'
_description = '报表模板-查询条件行'
diff --git a/yuthon_ai_agent/security/ir.model.access.csv b/yuthon_ai_agent/security/ir.model.access.csv
index 45c15f221..9ba8e3f2c 100644
--- a/yuthon_ai_agent/security/ir.model.access.csv
+++ b/yuthon_ai_agent/security/ir.model.access.csv
@@ -29,3 +29,5 @@ access_ai_report_template_param_def_user,参数定义-用户只读,model_ai_repo
access_ai_report_template_param_def_admin,参数定义-管理员,model_ai_report_template_param_def,yuthon_ai_agent.group_ai_agent_admin,1,1,1,1
access_ai_report_template_domain_line_user,查询条件行-用户只读,model_ai_report_template_domain_line,yuthon_ai_agent.group_ai_agent_user,1,0,0,0
access_ai_report_template_domain_line_admin,查询条件行-管理员,model_ai_report_template_domain_line,yuthon_ai_agent.group_ai_agent_admin,1,1,1,1
+access_ai_report_template_chart_user,图表配置-用户只读,model_ai_report_template_chart,yuthon_ai_agent.group_ai_agent_user,1,0,0,0
+access_ai_report_template_chart_admin,图表配置-管理员,model_ai_report_template_chart,yuthon_ai_agent.group_ai_agent_admin,1,1,1,1
diff --git a/yuthon_ai_agent/static/src/html/report_template.html b/yuthon_ai_agent/static/src/html/report_template.html
index 51c3c2191..f48f1c702 100644
--- a/yuthon_ai_agent/static/src/html/report_template.html
+++ b/yuthon_ai_agent/static/src/html/report_template.html
@@ -5,6 +5,7 @@
{{REPORT_TITLE}} · 动态报表
+