mirror of
https://github.com/ZeroHawkeye/wordZero.git
synced 2025-09-26 20:01:17 +08:00
完善文档功能,添加页眉和页脚支持,并优化表格行复制属性的深拷贝逻辑
This commit is contained in:
@@ -180,17 +180,17 @@ func main() {
|
||||
// log.Printf("设置页面边距失败: %v", err)
|
||||
// }
|
||||
|
||||
// 5. 添加页眉页脚 - 暂时跳过,因为API可能尚未实现
|
||||
// 5. 添加页眉页脚
|
||||
fmt.Println("添加页眉页脚...")
|
||||
// err = doc.AddHeader("高级功能演示文档", "")
|
||||
// if err != nil {
|
||||
// log.Printf("添加页眉失败: %v", err)
|
||||
// }
|
||||
err = doc.AddHeader(document.HeaderFooterTypeDefault, "高级功能演示文档")
|
||||
if err != nil {
|
||||
log.Printf("添加页眉失败: %v", err)
|
||||
}
|
||||
|
||||
// err = doc.AddFooter("", "第{页码}页 共{总页数}页")
|
||||
// if err != nil {
|
||||
// log.Printf("添加页脚失败: %v", err)
|
||||
// }
|
||||
err = doc.AddFooterWithPageNumber(document.HeaderFooterTypeDefault, "", true)
|
||||
if err != nil {
|
||||
log.Printf("添加页脚失败: %v", err)
|
||||
}
|
||||
|
||||
// 6. 创建演示表格
|
||||
fmt.Println("创建演示表格...")
|
||||
|
@@ -345,8 +345,29 @@ func (t *Table) InsertRow(position int, data []string) error {
|
||||
// 复制第一行的单元格属性作为模板
|
||||
templateRow := t.Rows[0]
|
||||
for i := 0; i < colCount; i++ {
|
||||
// 深拷贝单元格属性
|
||||
var cellProps *TableCellProperties
|
||||
if templateRow.Cells[i].Properties != nil {
|
||||
cellProps = &TableCellProperties{}
|
||||
// 复制宽度
|
||||
if templateRow.Cells[i].Properties.TableCellW != nil {
|
||||
cellProps.TableCellW = &TableCellW{
|
||||
W: templateRow.Cells[i].Properties.TableCellW.W,
|
||||
Type: templateRow.Cells[i].Properties.TableCellW.Type,
|
||||
}
|
||||
}
|
||||
// 复制垂直对齐
|
||||
if templateRow.Cells[i].Properties.VAlign != nil {
|
||||
cellProps.VAlign = &VAlign{
|
||||
Val: templateRow.Cells[i].Properties.VAlign.Val,
|
||||
}
|
||||
}
|
||||
// 复制其他必要的属性
|
||||
// 注意:不要复制GridSpan和VMerge,因为这些是合并相关的属性
|
||||
}
|
||||
|
||||
newRow.Cells[i] = TableCell{
|
||||
Properties: templateRow.Cells[i].Properties, // 复用属性
|
||||
Properties: cellProps,
|
||||
Paragraphs: []Paragraph{
|
||||
{
|
||||
Runs: []Run{
|
||||
|
305
test/table_insert_merge_fix_test.go
Normal file
305
test/table_insert_merge_fix_test.go
Normal file
@@ -0,0 +1,305 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ZeroHawkeye/wordZero/pkg/document"
|
||||
)
|
||||
|
||||
// TestTableInsertAndMergeFix 测试动态添加行后合并单元格的修复
|
||||
func TestTableInsertAndMergeFix(t *testing.T) {
|
||||
// 开启日志
|
||||
document.SetGlobalLevel(document.LogLevelInfo)
|
||||
|
||||
t.Run("验证属性深拷贝", func(t *testing.T) {
|
||||
doc := document.New()
|
||||
|
||||
// 创建初始表格
|
||||
config := &document.TableConfig{
|
||||
Rows: 2,
|
||||
Cols: 3,
|
||||
Width: 6000,
|
||||
}
|
||||
|
||||
table := doc.AddTable(config)
|
||||
if table == nil {
|
||||
t.Fatal("创建表格失败")
|
||||
}
|
||||
|
||||
// 设置第一行的单元格文本
|
||||
table.SetCellText(0, 0, "Header1")
|
||||
table.SetCellText(0, 1, "Header2")
|
||||
table.SetCellText(0, 2, "Header3")
|
||||
|
||||
// 添加新行
|
||||
err := table.AppendRow([]string{"Row2-1", "Row2-2", "Row2-3"})
|
||||
if err != nil {
|
||||
t.Fatalf("添加行失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取第一行和新添加行的单元格属性
|
||||
cell1, _ := table.GetCell(0, 0)
|
||||
cell2, _ := table.GetCell(2, 0)
|
||||
|
||||
// 验证属性是独立的(不是同一个指针)
|
||||
if cell1.Properties == cell2.Properties {
|
||||
t.Error("单元格属性应该是独立的副本,而不是共享的指针")
|
||||
}
|
||||
|
||||
// 修改新行的属性,不应影响第一行
|
||||
if cell2.Properties != nil && cell2.Properties.TableCellW != nil {
|
||||
cell2.Properties.TableCellW.W = "3000"
|
||||
}
|
||||
|
||||
// 验证第一行的属性没有被改变
|
||||
if cell1.Properties != nil && cell1.Properties.TableCellW != nil {
|
||||
if cell1.Properties.TableCellW.W == "3000" {
|
||||
t.Error("修改新行的属性不应该影响第一行")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("大表格动态添加和合并", func(t *testing.T) {
|
||||
doc := document.New()
|
||||
|
||||
// 创建28行的表格
|
||||
config := &document.TableConfig{
|
||||
Rows: 28,
|
||||
Cols: 5,
|
||||
Width: 10000,
|
||||
}
|
||||
|
||||
table := doc.AddTable(config)
|
||||
if table == nil {
|
||||
t.Fatal("创建表格失败")
|
||||
}
|
||||
|
||||
// 填充数据
|
||||
for i := 0; i < 28; i++ {
|
||||
for j := 0; j < 5; j++ {
|
||||
table.SetCellText(i, j, fmt.Sprintf("Cell-%d-%d", i+1, j+1))
|
||||
}
|
||||
}
|
||||
|
||||
// 动态添加多行
|
||||
for i := 29; i <= 35; i++ {
|
||||
rowData := make([]string, 5)
|
||||
for j := 0; j < 5; j++ {
|
||||
rowData[j] = fmt.Sprintf("Cell-%d-%d", i, j+1)
|
||||
}
|
||||
err := table.AppendRow(rowData)
|
||||
if err != nil {
|
||||
t.Fatalf("添加第%d行失败: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 在不同位置进行合并
|
||||
testCases := []struct {
|
||||
name string
|
||||
row int
|
||||
startCol int
|
||||
endCol int
|
||||
}{
|
||||
{"合并第1行", 0, 1, 3},
|
||||
{"合并第15行", 14, 0, 2},
|
||||
{"合并第28行", 27, 2, 4},
|
||||
{"合并第30行(动态添加的)", 29, 1, 3},
|
||||
{"合并最后一行", 34, 0, 1},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := table.MergeCellsHorizontal(tc.row, tc.startCol, tc.endCol)
|
||||
if err != nil {
|
||||
t.Errorf("%s失败: %v", tc.name, err)
|
||||
}
|
||||
|
||||
// 验证合并后的单元格数
|
||||
row := table.Rows[tc.row]
|
||||
expectedCells := 5 - (tc.endCol - tc.startCol)
|
||||
if len(row.Cells) != expectedCells {
|
||||
t.Errorf("%s后单元格数不正确: 期望%d,实际%d",
|
||||
tc.name, expectedCells, len(row.Cells))
|
||||
}
|
||||
|
||||
// 验证GridSpan设置
|
||||
cell, _ := table.GetCell(tc.row, tc.startCol)
|
||||
if cell.Properties == nil || cell.Properties.GridSpan == nil {
|
||||
t.Errorf("%s后GridSpan未设置", tc.name)
|
||||
} else {
|
||||
expectedSpan := fmt.Sprintf("%d", tc.endCol-tc.startCol+1)
|
||||
if cell.Properties.GridSpan.Val != expectedSpan {
|
||||
t.Errorf("%s后GridSpan值不正确: 期望%s,实际%s",
|
||||
tc.name, expectedSpan, cell.Properties.GridSpan.Val)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 保存文档
|
||||
err := doc.Save("test/output/large_table_merge_test.docx")
|
||||
if err != nil {
|
||||
t.Errorf("保存文档失败: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("混合操作测试", func(t *testing.T) {
|
||||
doc := document.New()
|
||||
|
||||
// 创建初始表格
|
||||
config := &document.TableConfig{
|
||||
Rows: 5,
|
||||
Cols: 4,
|
||||
Width: 8000,
|
||||
}
|
||||
|
||||
table := doc.AddTable(config)
|
||||
if table == nil {
|
||||
t.Fatal("创建表格失败")
|
||||
}
|
||||
|
||||
// 先合并一些单元格
|
||||
err := table.MergeCellsHorizontal(1, 1, 2)
|
||||
if err != nil {
|
||||
t.Fatalf("初始合并失败: %v", err)
|
||||
}
|
||||
|
||||
// 添加新行
|
||||
for i := 0; i < 3; i++ {
|
||||
err := table.AppendRow([]string{"New1", "New2", "New3", "New4"})
|
||||
if err != nil {
|
||||
t.Fatalf("添加行失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 在新添加的行上进行合并
|
||||
err = table.MergeCellsHorizontal(6, 0, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("合并新行失败: %v", err)
|
||||
}
|
||||
|
||||
// 验证表格结构完整性
|
||||
if table.GetRowCount() != 8 {
|
||||
t.Errorf("表格行数不正确: 期望8,实际%d", table.GetRowCount())
|
||||
}
|
||||
|
||||
// 验证每行的单元格数是否正确
|
||||
expectedCellCounts := []int{4, 3, 4, 4, 4, 4, 3, 4} // 第1行和第6行有合并
|
||||
for i, row := range table.Rows {
|
||||
if len(row.Cells) != expectedCellCounts[i] {
|
||||
t.Errorf("第%d行单元格数不正确: 期望%d,实际%d",
|
||||
i, expectedCellCounts[i], len(row.Cells))
|
||||
}
|
||||
}
|
||||
|
||||
// 保存文档
|
||||
err = doc.Save("test/output/mixed_operations_test.docx")
|
||||
if err != nil {
|
||||
t.Errorf("保存文档失败: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestTableGridConsistencyAfterFix 测试修复后的表格网格一致性
|
||||
func TestTableGridConsistencyAfterFix(t *testing.T) {
|
||||
doc := document.New()
|
||||
|
||||
// 创建带有自定义列宽的表格
|
||||
config := &document.TableConfig{
|
||||
Rows: 3,
|
||||
Cols: 4,
|
||||
Width: 8000,
|
||||
ColWidths: []int{1500, 2000, 2500, 2000},
|
||||
}
|
||||
|
||||
table := doc.AddTable(config)
|
||||
if table == nil {
|
||||
t.Fatal("创建表格失败")
|
||||
}
|
||||
|
||||
// 记录原始列宽
|
||||
originalWidths := make([]string, len(table.Grid.Cols))
|
||||
for i, col := range table.Grid.Cols {
|
||||
originalWidths[i] = col.W
|
||||
}
|
||||
|
||||
// 动态添加10行
|
||||
for i := 0; i < 10; i++ {
|
||||
err := table.AppendRow([]string{
|
||||
fmt.Sprintf("A%d", i+4),
|
||||
fmt.Sprintf("B%d", i+4),
|
||||
fmt.Sprintf("C%d", i+4),
|
||||
fmt.Sprintf("D%d", i+4),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("添加第%d行失败: %v", i+4, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 验证所有行的单元格宽度与网格定义一致
|
||||
for i, row := range table.Rows {
|
||||
for j, cell := range row.Cells {
|
||||
if cell.Properties == nil || cell.Properties.TableCellW == nil {
|
||||
t.Errorf("行%d列%d缺少宽度属性", i, j)
|
||||
continue
|
||||
}
|
||||
|
||||
expectedWidth := originalWidths[j]
|
||||
actualWidth := cell.Properties.TableCellW.W
|
||||
if actualWidth != expectedWidth {
|
||||
t.Errorf("行%d列%d宽度不一致: 期望%s,实际%s",
|
||||
i, j, expectedWidth, actualWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 进行一些合并操作
|
||||
err := table.MergeCellsHorizontal(5, 1, 2)
|
||||
if err != nil {
|
||||
t.Fatalf("合并失败: %v", err)
|
||||
}
|
||||
|
||||
err = table.MergeCellsVertical(8, 10, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("垂直合并失败: %v", err)
|
||||
}
|
||||
|
||||
// 再次验证未合并单元格的宽度保持一致
|
||||
for i, row := range table.Rows {
|
||||
cellIndex := 0
|
||||
for j := 0; j < len(originalWidths); j++ {
|
||||
if cellIndex >= len(row.Cells) {
|
||||
break
|
||||
}
|
||||
|
||||
cell := row.Cells[cellIndex]
|
||||
|
||||
// 跳过被合并掉的单元格
|
||||
if i == 5 && (j == 2 || j == 3) {
|
||||
// 这些单元格在第5行被水平合并了
|
||||
continue
|
||||
}
|
||||
|
||||
if cell.Properties != nil && cell.Properties.TableCellW != nil {
|
||||
expectedWidth := originalWidths[j]
|
||||
actualWidth := cell.Properties.TableCellW.W
|
||||
if actualWidth != expectedWidth {
|
||||
// 合并的单元格可能有不同的宽度
|
||||
if cell.Properties.GridSpan == nil && cell.Properties.VMerge == nil {
|
||||
t.Errorf("行%d单元格%d宽度不一致: 期望%s,实际%s",
|
||||
i, cellIndex, expectedWidth, actualWidth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cellIndex++
|
||||
}
|
||||
}
|
||||
|
||||
// 保存文档
|
||||
err = doc.Save("test/output/grid_consistency_after_fix.docx")
|
||||
if err != nil {
|
||||
t.Errorf("保存文档失败: %v", err)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user