跳转至

模板高级语法

本页面介绍Symphra Excel模板系统的高级语法特性,包括图片占位符、条件渲染和循环等。

概述

Symphra Excel模板系统支持丰富的语法特性,可以创建动态、交互式的Excel模板。除了基本的变量替换,还支持图片插入、条件判断和循环迭代。

图片占位符语法

基本语法

在模板中使用{% img %}标签插入图片:

# 模板单元格内容
{% img logo %}

带参数的图片语法

# 指定尺寸
{% img logo, width=100, height=50 %}

# 指定位置和边距
{% img product_image, width=120, height=80, margin=10 %}

# 同时指定多个参数
{% img banner, width=200, height=100, margin=8, position=center %}

条件渲染

基本条件

# 模板单元格内容
{% if show_title %}标题{% endif %}

# 带变量的条件
{% if status == 'active' %}有效{% else %}无效{% endif %}

多条件判断

# 模板单元格内容
{% if user_type == 'vip' %}
  尊贵的VIP用户
{% elif user_type == 'premium' %}
  高级会员
{% else %}
  普通用户
{% endif %}

数值条件

# 模板单元格内容
{% if score >= 90 %}
  优秀
{% elif score >= 60 %}
  及格
{% else %}
  不及格
{% endif %}

循环迭代

基本循环

# 模板单元格内容
{% for item in items %}
  {{ item }}
{% endfor %}

循环索引

# 模板单元格内容
{% for item in items %}
  {{ loop.index }}. {{ item }}
{% endfor %}

嵌套循环

# 模板单元格内容
{% for category in categories %}
  分类: {{ category.name }}
  {% for item in category.items %}
    - {{ item.name }}
  {% endfor %}
{% endfor %}

完整示例

示例1: 产品目录模板

模板设计:

A1: {% img company_logo, width=150, height=60 %}
A2: 产品目录
A4: 编号    名称          价格      图片
{% for product in products %}
A{{ loop.index + 4 }}: {{ product.id }}
B{{ loop.index + 4 }}: {{ product.name }}
C{{ loop.index + 4 }}: {{ product.price }}
D{{ loop.index + 4 }}: {% img {{ product.image_path }}, width=80, height=80 %}
{% endfor %}

渲染代码:

from symphra_excel import TemplateWorkbook
from symphra_excel.utils.format_fix import fix_xlsx

data = {
    "company_logo": "assets/logo.png",
    "products": [
        {
            "id": "P001",
            "name": "笔记本电脑",
            "price": 5999.99,
            "image_path": "images/laptop.png"
        },
        {
            "id": "P002",
            "name": "无线鼠标",
            "price": 89.99,
            "image_path": "images/mouse.png"
        },
        {
            "id": "P003",
            "name": "机械键盘",
            "price": 299.99,
            "image_path": "images/keyboard.png"
        },
    ]
}

with TemplateWorkbook("product_template.xlsx") as template:
    template.render(data)
    template.save("product_catalog.xlsx")

fix_xlsx("product_catalog.xlsx")

示例2: 销售报告模板

模板设计:

A1: {% img report_header, width=200, height=80 %}
A2: {{ report_title }}
A3: 报告日期: {{ report_date }}
A5: 销售人员业绩
A6: 姓名        销售额       状态
{% for sales in sales_data %}
A{{ loop.index + 6 }}: {{ sales.name }}
B{{ loop.index + 6 }}: {{ sales.amount }}
C{{ loop.index + 6 }}: {% if sales.amount >= 100000 %}优秀{% elif sales.amount >= 50000 %}良好{% else %}待提升{% endif %}
{% endfor %}

渲染代码:

from symphra_excel import TemplateWorkbook
from symphra_excel.utils.format_fix import fix_xlsx

data = {
    "report_header": "assets/report_banner.png",
    "report_title": "2024年第一季度销售报告",
    "report_date": "2024-03-31",
    "sales_data": [
        {"name": "张三", "amount": 120000},
        {"name": "李四", "amount": 95000},
        {"name": "王五", "amount": 45000},
    ]
}

with TemplateWorkbook("sales_template.xlsx") as template:
    template.render(data)
    template.save("sales_report.xlsx")

fix_xlsx("sales_report.xlsx")

示例3: 财务报表模板

模板设计:

A1: {% img company_logo, width=120, height=50 %}
A2: {{ company_name }} - 财务报表
A3: 期间: {{ period }}
A5: 项目              金额          备注
A6: 营业收入          {{ revenue }}  {% if revenue > 1000000 %}业绩突出{% endif %}
A7: 营业成本          {{ cost }}
A8: 毛利润            {{ profit }}   {% if profit_margin >= 0.3 %}毛利率健康{% elif profit_margin < 0.1 %}需要关注{% endif %}
A9: 净利润            {{ net_profit }}
A10: {% if net_profit > 0 %}盈利状况良好{% else %}亏损,需要改进{% endif %}

渲染代码:

from symphra_excel import TemplateWorkbook
from symphra_excel.utils.format_fix import fix_xlsx

data = {
    "company_logo": "assets/company_logo.png",
    "company_name": "ABC科技有限公司",
    "period": "2024年第一季度",
    "revenue": 1500000,
    "cost": 900000,
    "profit": 600000,
    "net_profit": 450000,
    "profit_margin": 0.4
}

with TemplateWorkbook("financial_template.xlsx") as template:
    template.render(data)
    template.save("financial_report.xlsx")

fix_xlsx("financial_report.xlsx")

高级特性

图片尺寸计算

# 自动计算尺寸
{% img product_image, width={{ base_width * 2 }}, height={{ base_height * 2 }}

# 根据条件调整
{% img status_icon, width={% if is_mobile %}50{% else %}100{% endif %}

动态图片路径

# 根据变量构建路径
{% img {{ category }}/{{ item_id }}.png %}

# 根据条件选择图片
{% img {% if status == 'success' %}icons/check.png{% else %}icons/error.png{% endif %}

循环中的图片

{% for item in items %}
A{{ loop.index }}: {{ item.name }}
B{{ loop.index }}: {% img {{ item.image_folder }}/{{ item.id }}.png, width=60, height=60 %}
{% endfor %}

模板变量检查

获取模板中的所有变量

from symphra_excel.templates.template_workbook import TemplateWorkbook

template = TemplateWorkbook.load_template("template.xlsx")
variables = template.get_template_variables()

print("模板需要的变量:")
for var in sorted(variables):
    print(f"  - {var}")

验证模板完整性

def validate_template(template_path: str, context: dict) -> bool:
    """验证模板和上下文是否匹配"""
    template = TemplateWorkbook.load_template(template_path)
    required_vars = template.get_template_variables()
    provided_vars = set(context.keys())

    missing_vars = required_vars - provided_vars
    if missing_vars:
        print(f"缺少变量: {missing_vars}")
        return False

    print("模板验证通过")
    return True

# 使用
is_valid = validate_template("template.xlsx", {
    "company_logo": "logo.png",
    "title": "报告",
})

性能优化

大数据量处理

# 分批处理大数据
def render_large_template(template_path: str, data_batch: list) -> None:
    """分批渲染大数据模板"""
    for i, data in enumerate(data_batch):
        with TemplateWorkbook(template_path) as template:
            template.render(data)
            template.save(f"output_{i:03d}.xlsx")

        # 清理内存
        import gc
        gc.collect()

# 使用
data_batches = [batch1, batch2, batch3]
render_large_template("template.xlsx", data_batches)

模板缓存

# 缓存模板以提高性能
template_cache = {}

def get_cached_template(template_path: str) -> TemplateWorkbook:
    """获取缓存的模板"""
    if template_path not in template_cache:
        template_cache[template_path] = TemplateWorkbook.load_template(template_path)
    return template_cache[template_path]

# 使用
template = get_cached_template("template.xlsx")
template.render(data)

错误处理

常见错误及解决方案

# 1. 图片文件不存在
try:
    template.render(data)
except FileNotFoundError as e:
    print(f"图片文件不存在: {e}")
    # 解决方案:检查图片路径

# 2. 模板变量缺失
required_vars = template.get_template_variables()
provided_vars = set(data.keys())

if not required_vars.issubset(provided_vars):
    missing = required_vars - provided_vars
    raise ValueError(f"缺少必要的模板变量: {missing}")

# 3. 循环变量类型错误
for item in data.get("items", []):
    if not isinstance(item, dict):
        raise TypeError(f"循环变量必须是字典类型: {item}")

最佳实践

✅ 推荐做法

# 1. 明确指定图片参数
{% img logo, width=100, height=50, margin=8 %}

# 2. 使用有意义的变量名
# 模板中
{% img company_logo %}
{% img product_image %}

# 数据中
data = {
    "company_logo": "assets/logo.png",
    "product_image": "images/product1.png",
}

# 3. 预先验证模板
template = TemplateWorkbook.load_template("template.xlsx")
variables = template.get_template_variables()
print(f"模板需要的变量: {variables}")

# 4. 使用修复工具
from symphra_excel.utils.format_fix import fix_xlsx

template.save("output.xlsx")
fix_xlsx("output.xlsx")  # 重要!

❌ 避免的做法

# 1. 图片路径不正确
{% img wrong/path/logo.png %}

# 2. 缺少图片参数导致变形
{% img large_image %}  # 没有指定width/height

# 3. 不检查模板变量
template.render(data)  # 可能因为缺少变量失败

# 4. 不调用fix_xlsx
template.save("output.xlsx")
# 缺少 fix_xlsx("output.xlsx")

调试技巧

启用详细日志

import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger("symphra_excel.templates")

# 渲染模板时会输出详细日志
template.render(data)

预览模板内容

def preview_template(template_path: str) -> None:
    """预览模板中的所有内容"""
    template = TemplateWorkbook.load_template(template_path)
    for sheet_name in template.sheetnames:
        sheet = template.get_sheet_by_name(sheet_name)
        print(f"\n工作表: {sheet_name}")
        used_range = sheet.get_used_range()
        values = sheet.get_range_values(used_range)
        for row in values:
            print("  ", row)

# 使用
preview_template("template.xlsx")

下一步