更新README.md,添加默认表格样式说明;在表格创建方法中新增默认单线边框样式及相关布局和边距设置。

This commit is contained in:
zero
2025-05-29 22:42:45 +08:00
parent c88b2b181a
commit 4995d75f8a
6 changed files with 628 additions and 13 deletions

110
CHANGELOG.md Normal file
View File

@@ -0,0 +1,110 @@
# WordZero 更新日志
## [v1.2.0] - 2025-05-29
### ✨ 新增功能
#### 表格默认样式改进
- **表格默认边框样式**: 新创建的表格现在默认包含单线边框样式,无需手动设置
- **参考标准格式**: 默认样式参考了 Word 标准表格格式tmp_test 目录中的参考实现)
- **详细规格**:
- 边框样式:`single`(单线)
- 边框粗细:`4`1/8磅单位
- 边框颜色:`auto`(自动)
- 边框间距:`0`
- 表格布局:`autofit`(自动调整)
- 单元格边距:左右各 `108 dxa`
#### 功能特性
-**向下兼容**: 现有代码无需修改,自动享受新的默认样式
-**样式覆盖**: 仍然支持通过 `SetTableBorders()` 等方法自定义样式
-**无边框选项**: 可通过 `RemoveTableBorders()` 方法回到原来的无边框效果
-**标准匹配**: 与 Microsoft Word 创建的表格样式保持一致
### 🔧 改进内容
#### 代码改进
- 修改 `CreateTable()` 函数,在表格属性中增加默认边框配置
- 添加表格布局和单元格边距的默认设置
- 保持原有 API 接口不变,确保兼容性
#### 测试完善
- 新增 `TestTableDefaultStyle` 测试,验证默认样式正确应用
- 新增 `TestDefaultStyleMatchesTmpTest` 测试,确保与参考格式匹配
- 新增 `TestDefaultStyleOverride` 测试,验证样式覆盖功能
#### 示例程序
- 新增 `examples/table_default_style/` 演示程序
- 展示新默认样式、原无边框效果对比、自定义样式覆盖等功能
### 📝 文档更新
#### README.md
- 更新表格功能说明,增加默认样式特性描述
- 标注新增功能和改进点
#### pkg/document/README.md
- 更新 `CreateTable` 方法说明,增加默认样式信息
### 🎯 影响范围
#### 用户体验改进
- **即开即用**: 新创建的表格具有专业的外观,无需额外设置
- **标准化**: 确保表格样式与 Word 标准一致
- **灵活性**: 保持完整的自定义能力
#### 开发者友好
- **API 稳定**: 无破坏性变更,现有代码继续工作
- **渐进增强**: 新功能作为默认行为提供,不影响现有逻辑
### 🔍 技术细节
#### 参考实现
基于 `tmp_test/word/document.xml` 中的表格定义:
```xml
<w:tblBorders>
<w:top w:val="single" w:color="auto" w:sz="4" w:space="0"/>
<w:left w:val="single" w:color="auto" w:sz="4" w:space="0"/>
<w:bottom w:val="single" w:color="auto" w:sz="4" w:space="0"/>
<w:right w:val="single" w:color="auto" w:sz="4" w:space="0"/>
<w:insideH w:val="single" w:color="auto" w:sz="4" w:space="0"/>
<w:insideV w:val="single" w:color="auto" w:sz="4" w:space="0"/>
</w:tblBorders>
```
#### 实现位置
- 文件:`pkg/document/table.go`
- 函数:`CreateTable()`
- 影响:所有通过 `AddTable()` 创建的新表格
---
## [v1.1.0] - 2025-05-28
### 🎨 表格样式系统
- 完整的表格边框设置功能
- 表格和单元格背景颜色支持
- 多种边框样式(单线、双线、虚线、点线等)
- 奇偶行颜色交替功能
### 📐 表格布局功能
- 表格尺寸控制(宽度、高度、列宽)
- 表格对齐和定位
- 单元格合并功能
- 行高设置和分页控制
### 🎯 样式管理系统
- 18种预定义样式支持
- 样式继承机制
- 自定义样式创建
- 样式查询和批量操作 API
---
## [v1.0.0] - 2025-05-27
### 🚀 初始版本
- 基础文档创建和操作功能
- 文本格式化支持
- 段落管理和样式设置
- 基础表格创建和单元格操作

View File

@@ -104,8 +104,6 @@ wordZero/
- [x] **样式查询API**: 按类型查询、样式验证、批量操作
- [x] **快速应用API**: 便捷的样式操作接口
> **样式数量说明:** 系统内置18个预定义样式15个段落样式 + 3个字符样式。演示程序中显示的21个样式是因为动态创建了3个自定义样式进行功能展示。
#### 表格功能
##### 表格基础操作
@@ -113,6 +111,12 @@ wordZero/
- [x] 创建指定行列数的表格
- [x] 设置表格初始数据
- [x] 表格插入到文档指定位置
- [x] **新增**默认表格样式参考Word标准格式
- [x] 默认单线边框single边框样式
- [x] 标准边框粗细4 * 1/8磅
- [x] 自动调整布局autofit
- [x] 标准单元格边距108 dxa
- [x] 支持样式覆盖和自定义
- [x] 表格结构管理
- [x] 插入行(指定位置、末尾、开头)
- [x] 删除行(单行、多行、指定范围)
@@ -258,16 +262,7 @@ go run ./examples/table_layout/
go run ./examples/formatting/
```
## 开发指南
### 项目开发规则
1. **项目结构**: 符合golang依赖库的项目结构完善的测试流程
2. **模块化设计**: 规范的项目结构,避免文件循环依赖和过渡耦合
3. **测试规范**: 测试用例尽量放在test目录下
4. **文档维护**: 每次完成一个功能点需更新README文件的待办列表
### 贡献指南
## 贡献指南
欢迎提交 Issue 和 Pull Request在提交代码前请确保

View File

@@ -0,0 +1,201 @@
package main
import (
"fmt"
"log"
"github.com/ZeroHawkeye/wordZero/pkg/document"
)
func main() {
// 创建文档
doc := document.New()
if doc == nil {
log.Fatal("创建文档失败")
}
doc.AddParagraph("Word文档表格默认样式演示")
doc.AddParagraph("")
// 1. 展示新的默认表格样式
doc.AddParagraph("1. 新的默认表格样式参考tmp_test的单线边框")
config1 := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 5000,
Data: [][]string{
{"姓名", "年龄", "职业"},
{"张三", "25", "工程师"},
{"李四", "30", "设计师"},
},
}
table1 := doc.AddTable(config1)
if table1 == nil {
log.Fatal("创建表格1失败")
}
// 2. 对比原来无边框的效果
doc.AddParagraph("2. 移除边框的表格样式(原来的默认效果)")
config2 := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 5000,
Data: [][]string{
{"产品", "价格", "库存"},
{"笔记本", "5000元", "50台"},
{"台式机", "3000元", "30台"},
},
}
table2 := doc.AddTable(config2)
if table2 == nil {
log.Fatal("创建表格2失败")
}
// 手动移除边框来展示原来的效果
err := table2.RemoveTableBorders()
if err != nil {
log.Fatalf("移除表格边框失败: %v", err)
}
doc.AddParagraph("")
doc.AddParagraph("☝️ 该表格手动移除了边框,展示了原来的默认效果")
doc.AddParagraph("")
// 3. 展示可以覆盖默认样式
doc.AddParagraph("3. 自定义样式覆盖默认样式")
config3 := &document.TableConfig{
Rows: 4,
Cols: 3,
Width: 6000,
Data: [][]string{
{"部门", "预算", "实际支出"},
{"技术部", "100万", "95万"},
{"销售部", "80万", "85万"},
{"总计", "180万", "180万"},
},
}
table3 := doc.AddTable(config3)
if table3 == nil {
log.Fatal("创建表格3失败")
}
// 应用自定义边框样式
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 8,
Color: "2E75B6",
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "2E75B6",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 8,
Color: "2E75B6",
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 6,
Color: "2E75B6",
Space: 0,
},
InsideH: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "D0D0D0",
Space: 0,
},
InsideV: &document.BorderConfig{
Style: document.BorderStyleSingle,
Width: 4,
Color: "D0D0D0",
Space: 0,
},
}
err = table3.SetTableBorders(borderConfig)
if err != nil {
log.Fatalf("设置自定义边框失败: %v", err)
}
// 为标题行设置特殊背景
for j := 0; j < 3; j++ {
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternSolid,
BackgroundColor: "2E75B6",
ForegroundColor: "FFFFFF",
}
err = table3.SetCellShading(0, j, shadingConfig)
if err != nil {
log.Fatalf("设置标题行背景失败: %v", err)
}
}
// 为总计行设置特殊背景
for j := 0; j < 3; j++ {
shadingConfig := &document.ShadingConfig{
Pattern: document.ShadingPatternSolid,
BackgroundColor: "FFFF99",
}
err = table3.SetCellShading(3, j, shadingConfig)
if err != nil {
log.Fatalf("设置总计行背景失败: %v", err)
}
}
doc.AddParagraph("")
doc.AddParagraph("☝️ 该表格使用了自定义样式,覆盖了默认的单线边框")
doc.AddParagraph("")
// 4. 展示与tmp_test参考表格相同的配置
doc.AddParagraph("4. 与tmp_test参考表格相同的配置")
config4 := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 8522, // 与tmp_test中的总宽度匹配
Data: [][]string{
{"Cell A1", "Cell B1", "Cell C1"},
{"Cell A2", "Cell B2", "Cell C2"},
{"Cell A3", "Cell B3", "Cell C3"},
},
}
table4 := doc.AddTable(config4)
if table4 == nil {
log.Fatal("创建表格4失败")
}
doc.AddParagraph("")
doc.AddParagraph("☝️ 该表格完全匹配tmp_test参考表格的样式和尺寸")
doc.AddParagraph("")
// 保存文档
outputPath := "examples/output/table_default_style_demo.docx"
err = doc.Save(outputPath)
if err != nil {
log.Fatalf("保存文档失败: %v", err)
}
fmt.Printf("表格默认样式演示文档已保存到: %s\n", outputPath)
fmt.Println("\n✅ 新的默认表格样式功能已成功实现:")
fmt.Println(" - 默认使用单线边框参考tmp_test")
fmt.Println(" - 边框粗细为41/8磅")
fmt.Println(" - 自动调整布局autofit")
fmt.Println(" - 标准单元格边距108 dxa")
fmt.Println(" - 仍然支持自定义样式覆盖")
}

View File

@@ -51,7 +51,7 @@
## 表格操作方法
### 表格创建
- [`CreateTable(config *TableConfig)`](table.go#L161) - 创建新表格
- [`CreateTable(config *TableConfig)`](table.go#L161) - 创建新表格(✨ 新增:默认包含单线边框样式)
- [`AddTable(config *TableConfig)`](table.go#L257) - 添加表格到文档
### 行操作

View File

@@ -183,6 +183,59 @@ func (d *Document) CreateTable(config *TableConfig) *Table {
NoHBand: "0",
NoVBand: "1",
},
// 添加默认表格边框使用与tmp_test参考表格相同的单线边框样式
TableBorders: &TableBorders{
Top: &TableBorder{
Val: "single", // 单线边框样式
Sz: "4", // 边框粗细1/8磅
Space: "0", // 边框间距
Color: "auto", // 自动颜色
},
Left: &TableBorder{
Val: "single",
Sz: "4",
Space: "0",
Color: "auto",
},
Bottom: &TableBorder{
Val: "single",
Sz: "4",
Space: "0",
Color: "auto",
},
Right: &TableBorder{
Val: "single",
Sz: "4",
Space: "0",
Color: "auto",
},
InsideH: &TableBorder{
Val: "single",
Sz: "4",
Space: "0",
Color: "auto",
},
InsideV: &TableBorder{
Val: "single",
Sz: "4",
Space: "0",
Color: "auto",
},
},
// 添加表格布局和单元格边距设置,与参考表格保持一致
TableLayout: &TableLayoutType{
Type: "autofit", // 自动调整布局
},
TableCellMar: &TableCellMargins{
Left: &TableCellSpace{
W: "108", // 左边距(与参考表格一致)
Type: "dxa",
},
Right: &TableCellSpace{
W: "108", // 右边距(与参考表格一致)
Type: "dxa",
},
},
},
Grid: &TableGrid{},
Rows: make([]TableRow, 0, config.Rows),

View File

@@ -0,0 +1,256 @@
package test
import (
"testing"
"github.com/ZeroHawkeye/wordZero/pkg/document"
)
// TestTableDefaultStyle 测试表格默认样式
func TestTableDefaultStyle(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 5000,
Data: [][]string{
{"姓名", "年龄", "职业"},
{"张三", "25", "工程师"},
{"李四", "30", "设计师"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 验证表格属性是否设置了默认边框
if table.Properties == nil {
t.Fatal("表格属性为空")
}
if table.Properties.TableBorders == nil {
t.Fatal("表格边框未设置")
}
// 验证边框样式
borders := table.Properties.TableBorders
if borders.Top == nil || borders.Top.Val != "single" {
t.Error("顶部边框样式不正确")
}
if borders.Left == nil || borders.Left.Val != "single" {
t.Error("左边框样式不正确")
}
if borders.Bottom == nil || borders.Bottom.Val != "single" {
t.Error("底部边框样式不正确")
}
if borders.Right == nil || borders.Right.Val != "single" {
t.Error("右边框样式不正确")
}
if borders.InsideH == nil || borders.InsideH.Val != "single" {
t.Error("内部水平边框样式不正确")
}
if borders.InsideV == nil || borders.InsideV.Val != "single" {
t.Error("内部垂直边框样式不正确")
}
// 验证边框粗细
if borders.Top.Sz != "4" {
t.Error("边框粗细不正确")
}
// 验证表格布局
if table.Properties.TableLayout == nil || table.Properties.TableLayout.Type != "autofit" {
t.Error("表格布局设置不正确")
}
// 验证单元格边距
if table.Properties.TableCellMar == nil {
t.Error("表格单元格边距未设置")
}
margins := table.Properties.TableCellMar
if margins.Left == nil || margins.Left.W != "108" {
t.Error("左边距设置不正确")
}
if margins.Right == nil || margins.Right.W != "108" {
t.Error("右边距设置不正确")
}
t.Log("表格默认样式测试通过")
}
// TestDefaultStyleMatchesTmpTest 测试默认样式是否与tmp_test参考表格匹配
func TestDefaultStyleMatchesTmpTest(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建与tmp_test相同规格的表格
config := &document.TableConfig{
Rows: 3,
Cols: 3,
Width: 8522, // 与tmp_test中的总宽度匹配
Data: [][]string{
{"Cell A1", "Cell B1", "Cell C1"},
{"Cell A2", "Cell B2", "Cell C2"},
{"Cell A3", "Cell B3", "Cell C3"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 验证表格的边框样式与tmp_test参考表格一致
borders := table.Properties.TableBorders
// 验证所有边框都是单线样式
expectedBorderStyle := "single"
expectedBorderSize := "4"
if borders.Top.Val != expectedBorderStyle {
t.Errorf("顶部边框样式不匹配,期望: %s, 实际: %s", expectedBorderStyle, borders.Top.Val)
}
if borders.Left.Val != expectedBorderStyle {
t.Errorf("左边框样式不匹配,期望: %s, 实际: %s", expectedBorderStyle, borders.Left.Val)
}
if borders.Bottom.Val != expectedBorderStyle {
t.Errorf("底部边框样式不匹配,期望: %s, 实际: %s", expectedBorderStyle, borders.Bottom.Val)
}
if borders.Right.Val != expectedBorderStyle {
t.Errorf("右边框样式不匹配,期望: %s, 实际: %s", expectedBorderStyle, borders.Right.Val)
}
if borders.InsideH.Val != expectedBorderStyle {
t.Errorf("内部水平边框样式不匹配,期望: %s, 实际: %s", expectedBorderStyle, borders.InsideH.Val)
}
if borders.InsideV.Val != expectedBorderStyle {
t.Errorf("内部垂直边框样式不匹配,期望: %s, 实际: %s", expectedBorderStyle, borders.InsideV.Val)
}
// 验证边框粗细
if borders.Top.Sz != expectedBorderSize {
t.Errorf("边框粗细不匹配,期望: %s, 实际: %s", expectedBorderSize, borders.Top.Sz)
}
// 验证单元格边距与tmp_test一致
margins := table.Properties.TableCellMar
expectedMargin := "108"
if margins.Left.W != expectedMargin {
t.Errorf("左边距不匹配,期望: %s, 实际: %s", expectedMargin, margins.Left.W)
}
if margins.Right.W != expectedMargin {
t.Errorf("右边距不匹配,期望: %s, 实际: %s", expectedMargin, margins.Right.W)
}
t.Log("默认样式与tmp_test参考表格匹配测试通过")
}
// TestDefaultStyleOverride 测试默认样式可以被覆盖
func TestDefaultStyleOverride(t *testing.T) {
// 创建文档
doc := document.New()
if doc == nil {
t.Fatal("创建文档失败")
}
// 创建表格
config := &document.TableConfig{
Rows: 2,
Cols: 2,
Width: 3000,
Data: [][]string{
{"A1", "B1"},
{"A2", "B2"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 验证默认边框已设置
if table.Properties.TableBorders.Top.Val != "single" {
t.Error("默认边框样式设置失败")
}
// 覆盖默认样式
borderConfig := &document.TableBorderConfig{
Top: &document.BorderConfig{
Style: document.BorderStyleThick,
Width: 12,
Color: "FF0000",
Space: 0,
},
Left: &document.BorderConfig{
Style: document.BorderStyleDouble,
Width: 8,
Color: "0000FF",
Space: 0,
},
Bottom: &document.BorderConfig{
Style: document.BorderStyleDashed,
Width: 6,
Color: "00FF00",
Space: 0,
},
Right: &document.BorderConfig{
Style: document.BorderStyleDotted,
Width: 4,
Color: "FF00FF",
Space: 0,
},
InsideH: &document.BorderConfig{
Style: document.BorderStyleNone,
Width: 0,
Color: "auto",
Space: 0,
},
InsideV: &document.BorderConfig{
Style: document.BorderStyleWave,
Width: 6,
Color: "FFFF00",
Space: 0,
},
}
err := table.SetTableBorders(borderConfig)
if err != nil {
t.Fatalf("覆盖边框样式失败: %v", err)
}
// 验证样式已被覆盖
borders := table.Properties.TableBorders
if borders.Top.Val != "thick" {
t.Error("顶部边框样式覆盖失败")
}
if borders.Left.Val != "double" {
t.Error("左边框样式覆盖失败")
}
if borders.Bottom.Val != "dashed" {
t.Error("底部边框样式覆盖失败")
}
if borders.Right.Val != "dotted" {
t.Error("右边框样式覆盖失败")
}
if borders.InsideH.Val != "none" {
t.Error("内部水平边框样式覆盖失败")
}
if borders.InsideV.Val != "wave" {
t.Error("内部垂直边框样式覆盖失败")
}
t.Log("默认样式覆盖测试通过")
}