mirror of
https://github.com/ZeroHawkeye/wordZero.git
synced 2025-09-26 20:01:17 +08:00
重构模板引擎,新增文档克隆和变量替换功能,优化模板渲染逻辑
This commit is contained in:
356
examples/enhanced_template_demo/enhanced_template_demo.go
Normal file
356
examples/enhanced_template_demo/enhanced_template_demo.go
Normal file
@@ -0,0 +1,356 @@
|
||||
// Package main 演示增强的模板功能
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/ZeroHawkeye/wordZero/pkg/document"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("=== WordZero 增强模板功能演示 ===")
|
||||
|
||||
// 确保输出目录存在
|
||||
os.MkdirAll("examples/output", 0755)
|
||||
|
||||
// 演示1:保持样式的变量替换
|
||||
demonstrateStyledVariableTemplate()
|
||||
|
||||
// 演示2:表格模板功能
|
||||
demonstrateTableTemplate()
|
||||
|
||||
// 演示3:复杂文档模板
|
||||
demonstrateComplexDocumentTemplate()
|
||||
|
||||
fmt.Println("\n✅ 增强模板功能演示完成!")
|
||||
}
|
||||
|
||||
// demonstrateStyledVariableTemplate 演示保持样式的变量替换
|
||||
func demonstrateStyledVariableTemplate() {
|
||||
fmt.Println("\n📝 演示1:保持样式的变量替换")
|
||||
|
||||
// 创建一个包含格式化内容的模板文档
|
||||
templateDoc := document.New()
|
||||
|
||||
// 添加标题
|
||||
titlePara := templateDoc.AddParagraph("")
|
||||
titleRun := &document.Run{
|
||||
Properties: &document.RunProperties{
|
||||
Bold: &document.Bold{},
|
||||
FontSize: &document.FontSize{Val: "32"}, // 16磅
|
||||
Color: &document.Color{Val: "0066CC"},
|
||||
},
|
||||
Text: document.Text{Content: "{{title}}"},
|
||||
}
|
||||
titlePara.Runs = []document.Run{*titleRun}
|
||||
titlePara.SetAlignment(document.AlignCenter)
|
||||
|
||||
// 添加副标题
|
||||
subtitlePara := templateDoc.AddParagraph("")
|
||||
subtitleRun := &document.Run{
|
||||
Properties: &document.RunProperties{
|
||||
Italic: &document.Italic{},
|
||||
FontSize: &document.FontSize{Val: "24"}, // 12磅
|
||||
Color: &document.Color{Val: "666666"},
|
||||
},
|
||||
Text: document.Text{Content: "作者:{{author}} | 日期:{{date}}"},
|
||||
}
|
||||
subtitlePara.Runs = []document.Run{*subtitleRun}
|
||||
subtitlePara.SetAlignment(document.AlignCenter)
|
||||
|
||||
// 添加正文段落
|
||||
bodyPara := templateDoc.AddParagraph("")
|
||||
|
||||
// 混合格式的正文
|
||||
normalRun := &document.Run{
|
||||
Text: document.Text{Content: "这是一个"},
|
||||
}
|
||||
boldRun := &document.Run{
|
||||
Properties: &document.RunProperties{
|
||||
Bold: &document.Bold{},
|
||||
},
|
||||
Text: document.Text{Content: "{{status}}"},
|
||||
}
|
||||
endRun := &document.Run{
|
||||
Text: document.Text{Content: "的项目,当前进度为"},
|
||||
}
|
||||
progressRun := &document.Run{
|
||||
Properties: &document.RunProperties{
|
||||
Bold: &document.Bold{},
|
||||
Color: &document.Color{Val: "FF0000"},
|
||||
},
|
||||
Text: document.Text{Content: "{{progress}}%"},
|
||||
}
|
||||
finalRun := &document.Run{
|
||||
Text: document.Text{Content: "。"},
|
||||
}
|
||||
|
||||
bodyPara.Runs = []document.Run{*normalRun, *boldRun, *endRun, *progressRun, *finalRun}
|
||||
|
||||
// 保存模板文档
|
||||
templateFile := "examples/output/styled_template.docx"
|
||||
err := templateDoc.Save(templateFile)
|
||||
if err != nil {
|
||||
log.Fatalf("保存模板文档失败: %v", err)
|
||||
}
|
||||
fmt.Printf("✓ 创建样式模板文档: %s\n", templateFile)
|
||||
|
||||
// 创建模板引擎并加载模板
|
||||
engine := document.NewTemplateEngine()
|
||||
_, err = engine.LoadTemplateFromDocument("styled_template", templateDoc)
|
||||
if err != nil {
|
||||
log.Fatalf("加载模板失败: %v", err)
|
||||
}
|
||||
|
||||
// 准备数据
|
||||
data := document.NewTemplateData()
|
||||
data.SetVariable("title", "WordZero 项目报告")
|
||||
data.SetVariable("author", "张开发")
|
||||
data.SetVariable("date", time.Now().Format("2006年01月02日"))
|
||||
data.SetVariable("status", "进行中")
|
||||
data.SetVariable("progress", "85")
|
||||
|
||||
// 使用新的渲染方法
|
||||
resultDoc, err := engine.RenderTemplateToDocument("styled_template", data)
|
||||
if err != nil {
|
||||
log.Fatalf("渲染模板失败: %v", err)
|
||||
}
|
||||
|
||||
// 保存结果文档
|
||||
outputFile := "examples/output/styled_result_" + time.Now().Format("20060102_150405") + ".docx"
|
||||
err = resultDoc.Save(outputFile)
|
||||
if err != nil {
|
||||
log.Fatalf("保存结果文档失败: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ 生成保持样式的文档: %s\n", outputFile)
|
||||
}
|
||||
|
||||
// demonstrateTableTemplate 演示表格模板功能
|
||||
func demonstrateTableTemplate() {
|
||||
fmt.Println("\n📊 演示2:表格模板功能")
|
||||
|
||||
// 创建包含表格模板的文档
|
||||
templateDoc := document.New()
|
||||
|
||||
// 添加标题
|
||||
templateDoc.AddHeadingParagraph("销售报表", 1)
|
||||
|
||||
// 创建表格模板
|
||||
tableConfig := &document.TableConfig{
|
||||
Rows: 2, // 表头 + 模板行
|
||||
Cols: 4,
|
||||
Width: 9000, // 15cm
|
||||
}
|
||||
table := templateDoc.CreateTable(tableConfig)
|
||||
|
||||
// 设置表头
|
||||
table.SetCellText(0, 0, "产品名称")
|
||||
table.SetCellText(0, 1, "销售数量")
|
||||
table.SetCellText(0, 2, "单价")
|
||||
table.SetCellText(0, 3, "总金额")
|
||||
|
||||
// 设置表头样式
|
||||
headerFormat := &document.TextFormat{
|
||||
Bold: true,
|
||||
FontSize: 12,
|
||||
FontColor: "FFFFFF",
|
||||
}
|
||||
|
||||
headerTexts := []string{"产品名称", "销售数量", "单价", "总金额"}
|
||||
for i := 0; i < 4; i++ {
|
||||
table.SetCellFormattedText(0, i, headerTexts[i], headerFormat)
|
||||
|
||||
// 设置表头背景色
|
||||
table.SetCellShading(0, i, &document.ShadingConfig{
|
||||
Pattern: document.ShadingPatternClear,
|
||||
BackgroundColor: "366092",
|
||||
})
|
||||
}
|
||||
|
||||
// 设置模板行(包含循环语法)
|
||||
table.SetCellText(1, 0, "{{#each items}}{{name}}")
|
||||
table.SetCellText(1, 1, "{{quantity}}")
|
||||
table.SetCellText(1, 2, "{{price}}")
|
||||
table.SetCellText(1, 3, "{{total}}{{/each}}")
|
||||
|
||||
// 添加表格到文档
|
||||
templateDoc.Body.AddElement(table)
|
||||
|
||||
// 保存模板文档
|
||||
templateFile := "examples/output/table_template.docx"
|
||||
err := templateDoc.Save(templateFile)
|
||||
if err != nil {
|
||||
log.Fatalf("保存表格模板失败: %v", err)
|
||||
}
|
||||
fmt.Printf("✓ 创建表格模板文档: %s\n", templateFile)
|
||||
|
||||
// 创建模板引擎并加载模板
|
||||
engine := document.NewTemplateEngine()
|
||||
_, err = engine.LoadTemplateFromDocument("table_template", templateDoc)
|
||||
if err != nil {
|
||||
log.Fatalf("加载表格模板失败: %v", err)
|
||||
}
|
||||
|
||||
// 准备销售数据
|
||||
data := document.NewTemplateData()
|
||||
|
||||
salesItems := []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "WordZero专业版",
|
||||
"quantity": "10",
|
||||
"price": "¥999.00",
|
||||
"total": "¥9,990.00",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "技术支持服务",
|
||||
"quantity": "12",
|
||||
"price": "¥500.00",
|
||||
"total": "¥6,000.00",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "培训课程",
|
||||
"quantity": "5",
|
||||
"price": "¥800.00",
|
||||
"total": "¥4,000.00",
|
||||
},
|
||||
}
|
||||
|
||||
data.SetList("items", salesItems)
|
||||
|
||||
// 渲染表格模板
|
||||
resultDoc, err := engine.RenderTemplateToDocument("table_template", data)
|
||||
if err != nil {
|
||||
log.Fatalf("渲染表格模板失败: %v", err)
|
||||
}
|
||||
|
||||
// 保存结果文档
|
||||
outputFile := "examples/output/table_result_" + time.Now().Format("20060102_150405") + ".docx"
|
||||
err = resultDoc.Save(outputFile)
|
||||
if err != nil {
|
||||
log.Fatalf("保存表格结果失败: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ 生成表格报表文档: %s\n", outputFile)
|
||||
}
|
||||
|
||||
// demonstrateComplexDocumentTemplate 演示复杂文档模板
|
||||
func demonstrateComplexDocumentTemplate() {
|
||||
fmt.Println("\n📋 演示3:复杂文档模板")
|
||||
|
||||
// 创建复杂的文档模板
|
||||
templateDoc := document.New()
|
||||
|
||||
// 注释:文档属性设置功能需要实现
|
||||
// templateDoc.SetProperty("title", "{{projectName}} - 项目报告")
|
||||
// templateDoc.SetProperty("author", "{{manager}}")
|
||||
// templateDoc.SetProperty("subject", "项目进度报告")
|
||||
|
||||
// 添加封面
|
||||
titlePara := templateDoc.AddParagraph("")
|
||||
titleRun := &document.Run{
|
||||
Properties: &document.RunProperties{
|
||||
Bold: &document.Bold{},
|
||||
FontSize: &document.FontSize{Val: "48"}, // 24磅
|
||||
Color: &document.Color{Val: "2F5496"},
|
||||
},
|
||||
Text: document.Text{Content: "{{projectName}}"},
|
||||
}
|
||||
titlePara.Runs = []document.Run{*titleRun}
|
||||
titlePara.SetAlignment(document.AlignCenter)
|
||||
titlePara.SetSpacing(&document.SpacingConfig{
|
||||
BeforePara: 72, // 1英寸
|
||||
AfterPara: 36, // 0.5英寸
|
||||
})
|
||||
|
||||
// 添加项目基本信息
|
||||
templateDoc.AddHeadingParagraph("项目基本信息", 2)
|
||||
|
||||
templateDoc.AddParagraph("项目经理:{{manager}}\n报告日期:{{reportDate}}\n项目状态:{{#if isActive}}进行中{{/if}}{{#if isComplete}}已完成{{/if}}\n完成进度:{{progress}}%")
|
||||
|
||||
// 添加团队成员表格
|
||||
templateDoc.AddHeadingParagraph("团队成员", 2)
|
||||
|
||||
teamTableConfig := &document.TableConfig{
|
||||
Rows: 2, // 表头 + 模板行
|
||||
Cols: 3,
|
||||
Width: 8000,
|
||||
}
|
||||
teamTable := templateDoc.CreateTable(teamTableConfig)
|
||||
|
||||
// 设置团队表格表头
|
||||
teamTable.SetCellText(0, 0, "姓名")
|
||||
teamTable.SetCellText(0, 1, "角色")
|
||||
teamTable.SetCellText(0, 2, "工作内容")
|
||||
|
||||
// 设置团队表格模板行
|
||||
teamTable.SetCellText(1, 0, "{{#each team}}{{name}}")
|
||||
teamTable.SetCellText(1, 1, "{{role}}")
|
||||
teamTable.SetCellText(1, 2, "{{work}}{{/each}}")
|
||||
|
||||
templateDoc.Body.AddElement(teamTable)
|
||||
|
||||
// 保存复杂模板
|
||||
templateFile := "examples/output/complex_template.docx"
|
||||
err := templateDoc.Save(templateFile)
|
||||
if err != nil {
|
||||
log.Fatalf("保存复杂模板失败: %v", err)
|
||||
}
|
||||
fmt.Printf("✓ 创建复杂文档模板: %s\n", templateFile)
|
||||
|
||||
// 创建模板引擎并渲染
|
||||
engine := document.NewTemplateEngine()
|
||||
_, err = engine.LoadTemplateFromDocument("complex_template", templateDoc)
|
||||
if err != nil {
|
||||
log.Fatalf("加载复杂模板失败: %v", err)
|
||||
}
|
||||
|
||||
// 准备复杂数据
|
||||
data := document.NewTemplateData()
|
||||
data.SetVariable("projectName", "WordZero 企业文档管理系统")
|
||||
data.SetVariable("manager", "李项目经理")
|
||||
data.SetVariable("reportDate", time.Now().Format("2006年01月02日"))
|
||||
data.SetVariable("progress", "88")
|
||||
|
||||
// 设置条件
|
||||
data.SetCondition("isActive", true)
|
||||
data.SetCondition("isComplete", false)
|
||||
|
||||
// 设置团队成员数据
|
||||
teamMembers := []interface{}{
|
||||
map[string]interface{}{
|
||||
"name": "张开发",
|
||||
"role": "技术负责人",
|
||||
"work": "架构设计与核心开发",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "王测试",
|
||||
"role": "质量保证",
|
||||
"work": "功能测试与性能优化",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"name": "刘设计",
|
||||
"role": "UI设计师",
|
||||
"work": "界面设计与用户体验",
|
||||
},
|
||||
}
|
||||
data.SetList("team", teamMembers)
|
||||
|
||||
// 渲染复杂文档
|
||||
resultDoc, err := engine.RenderTemplateToDocument("complex_template", data)
|
||||
if err != nil {
|
||||
log.Fatalf("渲染复杂模板失败: %v", err)
|
||||
}
|
||||
|
||||
// 保存结果
|
||||
outputFile := "examples/output/complex_result_" + time.Now().Format("20060102_150405") + ".docx"
|
||||
err = resultDoc.Save(outputFile)
|
||||
if err != nil {
|
||||
log.Fatalf("保存复杂结果失败: %v", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✓ 生成复杂项目报告: %s\n", outputFile)
|
||||
}
|
@@ -605,37 +605,14 @@ func (te *TemplateEngine) validateEachStatements(content string) error {
|
||||
|
||||
// documentToTemplateString 将文档转换为模板字符串
|
||||
func (te *TemplateEngine) documentToTemplateString(doc *Document) (string, error) {
|
||||
var content strings.Builder
|
||||
|
||||
// 遍历文档的所有段落,生成模板字符串
|
||||
for _, element := range doc.Body.Elements {
|
||||
switch elem := element.(type) {
|
||||
case *Paragraph:
|
||||
// 处理段落
|
||||
for _, run := range elem.Runs {
|
||||
content.WriteString(run.Text.Content)
|
||||
}
|
||||
content.WriteString("\n")
|
||||
case *Table:
|
||||
// 处理表格
|
||||
content.WriteString("{{#table}}\n")
|
||||
for i, row := range elem.Rows {
|
||||
content.WriteString(fmt.Sprintf("{{#row_%d}}\n", i))
|
||||
for j := range row.Cells {
|
||||
content.WriteString(fmt.Sprintf("{{cell_%d_%d}}", i, j))
|
||||
}
|
||||
content.WriteString("{{/row}}\n")
|
||||
}
|
||||
content.WriteString("{{/table}}\n")
|
||||
}
|
||||
}
|
||||
|
||||
return content.String(), nil
|
||||
// 这里不再转换为纯字符串,而是保持原始文档结构
|
||||
// 实际上我们应该直接在原文档上进行变量替换
|
||||
return "", nil // 将在新的方法中处理
|
||||
}
|
||||
|
||||
// cloneDocument 克隆文档
|
||||
func (te *TemplateEngine) cloneDocument(source *Document) *Document {
|
||||
// 简单实现:创建新文档并复制基本结构
|
||||
// 创建新文档
|
||||
doc := New()
|
||||
|
||||
// 复制样式管理器
|
||||
@@ -643,24 +620,318 @@ func (te *TemplateEngine) cloneDocument(source *Document) *Document {
|
||||
doc.styleManager = source.styleManager
|
||||
}
|
||||
|
||||
// 深度复制文档体元素
|
||||
for _, element := range source.Body.Elements {
|
||||
switch elem := element.(type) {
|
||||
case *Paragraph:
|
||||
// 复制段落
|
||||
newPara := &Paragraph{
|
||||
Properties: elem.Properties,
|
||||
Runs: make([]Run, len(elem.Runs)),
|
||||
}
|
||||
for i, run := range elem.Runs {
|
||||
newRun := Run{
|
||||
Properties: run.Properties,
|
||||
Text: Text{Content: run.Text.Content},
|
||||
}
|
||||
newPara.Runs[i] = newRun
|
||||
}
|
||||
doc.Body.Elements = append(doc.Body.Elements, newPara)
|
||||
|
||||
case *Table:
|
||||
// 复制表格
|
||||
newTable := &Table{
|
||||
Properties: elem.Properties,
|
||||
Grid: elem.Grid,
|
||||
Rows: make([]TableRow, len(elem.Rows)),
|
||||
}
|
||||
for i, row := range elem.Rows {
|
||||
newRow := TableRow{
|
||||
Properties: row.Properties,
|
||||
Cells: make([]TableCell, len(row.Cells)),
|
||||
}
|
||||
for j, cell := range row.Cells {
|
||||
newCell := TableCell{
|
||||
Properties: cell.Properties,
|
||||
Paragraphs: make([]Paragraph, len(cell.Paragraphs)),
|
||||
}
|
||||
for k, para := range cell.Paragraphs {
|
||||
newPara := Paragraph{
|
||||
Properties: para.Properties,
|
||||
Runs: make([]Run, len(para.Runs)),
|
||||
}
|
||||
for l, run := range para.Runs {
|
||||
newRun := Run{
|
||||
Properties: run.Properties,
|
||||
Text: Text{Content: run.Text.Content},
|
||||
}
|
||||
newPara.Runs[l] = newRun
|
||||
}
|
||||
newCell.Paragraphs[k] = newPara
|
||||
}
|
||||
newRow.Cells[j] = newCell
|
||||
}
|
||||
newTable.Rows[i] = newRow
|
||||
}
|
||||
doc.Body.Elements = append(doc.Body.Elements, newTable)
|
||||
}
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
// applyRenderedContentToDocument 将渲染内容应用到文档
|
||||
func (te *TemplateEngine) applyRenderedContentToDocument(doc *Document, content string) error {
|
||||
// 将渲染后的内容按行分割并添加到文档
|
||||
lines := strings.Split(content, "\n")
|
||||
// 这个方法将被新的结构化处理方法替代
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" {
|
||||
doc.AddParagraph(line)
|
||||
// RenderTemplateToDocument 渲染模板到新文档(新的主要方法)
|
||||
func (te *TemplateEngine) RenderTemplateToDocument(templateName string, data *TemplateData) (*Document, error) {
|
||||
template, err := te.GetTemplate(templateName)
|
||||
if err != nil {
|
||||
return nil, WrapErrorWithContext("render_template_to_document", err, templateName)
|
||||
}
|
||||
|
||||
// 如果有基础文档,克隆它并在其上进行变量替换
|
||||
if template.BaseDoc != nil {
|
||||
doc := te.cloneDocument(template.BaseDoc)
|
||||
|
||||
// 在文档结构中直接进行变量替换
|
||||
err := te.replaceVariablesInDocument(doc, data)
|
||||
if err != nil {
|
||||
return nil, WrapErrorWithContext("render_template_to_document", err, templateName)
|
||||
}
|
||||
|
||||
return doc, nil
|
||||
}
|
||||
|
||||
// 如果没有基础文档,使用原有的方式
|
||||
return te.RenderToDocument(templateName, data)
|
||||
}
|
||||
|
||||
// replaceVariablesInDocument 在文档结构中直接替换变量
|
||||
func (te *TemplateEngine) replaceVariablesInDocument(doc *Document, data *TemplateData) error {
|
||||
for _, element := range doc.Body.Elements {
|
||||
switch elem := element.(type) {
|
||||
case *Paragraph:
|
||||
// 处理段落中的变量替换
|
||||
err := te.replaceVariablesInParagraph(elem, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case *Table:
|
||||
// 处理表格中的变量替换和模板语法
|
||||
err := te.replaceVariablesInTable(elem, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// replaceVariablesInParagraph 在段落中替换变量
|
||||
func (te *TemplateEngine) replaceVariablesInParagraph(para *Paragraph, data *TemplateData) error {
|
||||
for i := range para.Runs {
|
||||
if para.Runs[i].Text.Content != "" {
|
||||
// 替换普通变量
|
||||
para.Runs[i].Text.Content = te.renderVariables(para.Runs[i].Text.Content, data.Variables)
|
||||
|
||||
// 处理条件语句
|
||||
para.Runs[i].Text.Content = te.renderConditionals(para.Runs[i].Text.Content, data.Conditions)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// replaceVariablesInTable 在表格中替换变量和处理表格模板
|
||||
func (te *TemplateEngine) replaceVariablesInTable(table *Table, data *TemplateData) error {
|
||||
// 检查是否有表格循环模板
|
||||
if len(table.Rows) > 0 && te.isTableTemplate(table) {
|
||||
return te.renderTableTemplate(table, data)
|
||||
}
|
||||
|
||||
// 普通表格变量替换
|
||||
for i := range table.Rows {
|
||||
for j := range table.Rows[i].Cells {
|
||||
for k := range table.Rows[i].Cells[j].Paragraphs {
|
||||
err := te.replaceVariablesInParagraph(&table.Rows[i].Cells[j].Paragraphs[k], data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isTableTemplate 检查表格是否包含模板语法
|
||||
func (te *TemplateEngine) isTableTemplate(table *Table) bool {
|
||||
if len(table.Rows) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查所有行是否包含循环语法
|
||||
for _, row := range table.Rows {
|
||||
for _, cell := range row.Cells {
|
||||
for _, para := range cell.Paragraphs {
|
||||
for _, run := range para.Runs {
|
||||
if run.Text.Content != "" && te.containsTemplateLoop(run.Text.Content) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// containsTemplateLoop 检查文本是否包含循环模板语法
|
||||
func (te *TemplateEngine) containsTemplateLoop(text string) bool {
|
||||
eachPattern := regexp.MustCompile(`\{\{#each\s+\w+\}\}`)
|
||||
return eachPattern.MatchString(text)
|
||||
}
|
||||
|
||||
// renderTableTemplate 渲染表格模板
|
||||
func (te *TemplateEngine) renderTableTemplate(table *Table, data *TemplateData) error {
|
||||
if len(table.Rows) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 找到模板行(包含循环语法的行)
|
||||
templateRowIndex := -1
|
||||
var listVarName string
|
||||
|
||||
for i, row := range table.Rows {
|
||||
found := false
|
||||
for _, cell := range row.Cells {
|
||||
for _, para := range cell.Paragraphs {
|
||||
for _, run := range para.Runs {
|
||||
if run.Text.Content != "" {
|
||||
eachPattern := regexp.MustCompile(`\{\{#each\s+(\w+)\}\}`)
|
||||
matches := eachPattern.FindStringSubmatch(run.Text.Content)
|
||||
if len(matches) > 1 {
|
||||
templateRowIndex = i
|
||||
listVarName = matches[1]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
if found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if templateRowIndex < 0 || listVarName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取列表数据
|
||||
listData, exists := data.Lists[listVarName]
|
||||
if !exists || len(listData) == 0 {
|
||||
// 删除模板行
|
||||
table.Rows = append(table.Rows[:templateRowIndex], table.Rows[templateRowIndex+1:]...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 保存模板行
|
||||
templateRow := table.Rows[templateRowIndex]
|
||||
newRows := make([]TableRow, 0)
|
||||
|
||||
// 保留模板行之前的行
|
||||
newRows = append(newRows, table.Rows[:templateRowIndex]...)
|
||||
|
||||
// 为每个数据项生成新行
|
||||
for _, item := range listData {
|
||||
newRow := te.cloneTableRow(&templateRow)
|
||||
|
||||
// 在新行中替换变量
|
||||
if itemMap, ok := item.(map[string]interface{}); ok {
|
||||
for i := range newRow.Cells {
|
||||
for j := range newRow.Cells[i].Paragraphs {
|
||||
for k := range newRow.Cells[i].Paragraphs[j].Runs {
|
||||
if newRow.Cells[i].Paragraphs[j].Runs[k].Text.Content != "" {
|
||||
// 移除模板语法标记
|
||||
content := newRow.Cells[i].Paragraphs[j].Runs[k].Text.Content
|
||||
content = regexp.MustCompile(`\{\{#each\s+\w+\}\}`).ReplaceAllString(content, "")
|
||||
content = regexp.MustCompile(`\{\{/each\}\}`).ReplaceAllString(content, "")
|
||||
|
||||
// 替换变量
|
||||
for key, value := range itemMap {
|
||||
placeholder := fmt.Sprintf("{{%s}}", key)
|
||||
content = strings.ReplaceAll(content, placeholder, te.interfaceToString(value))
|
||||
}
|
||||
|
||||
newRow.Cells[i].Paragraphs[j].Runs[k].Text.Content = content
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newRows = append(newRows, *newRow)
|
||||
}
|
||||
|
||||
// 保留模板行之后的行
|
||||
newRows = append(newRows, table.Rows[templateRowIndex+1:]...)
|
||||
|
||||
// 更新表格行
|
||||
table.Rows = newRows
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cloneTableRow 克隆表格行
|
||||
func (te *TemplateEngine) cloneTableRow(row *TableRow) *TableRow {
|
||||
newRow := &TableRow{
|
||||
Properties: row.Properties,
|
||||
Cells: make([]TableCell, len(row.Cells)),
|
||||
}
|
||||
|
||||
for i, cell := range row.Cells {
|
||||
newCell := TableCell{
|
||||
Properties: cell.Properties,
|
||||
Paragraphs: make([]Paragraph, len(cell.Paragraphs)),
|
||||
}
|
||||
|
||||
for j, para := range cell.Paragraphs {
|
||||
newPara := Paragraph{
|
||||
Properties: para.Properties,
|
||||
Runs: make([]Run, len(para.Runs)),
|
||||
}
|
||||
|
||||
for k, run := range para.Runs {
|
||||
newRun := Run{
|
||||
Properties: run.Properties,
|
||||
Text: Text{Content: run.Text.Content},
|
||||
}
|
||||
newPara.Runs[k] = newRun
|
||||
}
|
||||
|
||||
newCell.Paragraphs[j] = newPara
|
||||
}
|
||||
|
||||
newRow.Cells[i] = newCell
|
||||
}
|
||||
|
||||
return newRow
|
||||
}
|
||||
|
||||
// NewTemplateData 创建新的模板数据
|
||||
func NewTemplateData() *TemplateData {
|
||||
return &TemplateData{
|
||||
|
Reference in New Issue
Block a user