未验证 提交 866e7fd9 编写于 作者: B Bram Vanbilsen 提交者: GitHub

This closes #1729, support copy conditional format and data validation on duplicate row (#1733)

上级 bce2789c
......@@ -18,10 +18,24 @@ import (
"math"
"os"
"strconv"
"strings"
"github.com/mohae/deepcopy"
)
// duplicateHelperFunc defines functions to duplicate helper.
var duplicateHelperFunc = [3]func(*File, *xlsxWorksheet, string, int, int) error{
func(f *File, ws *xlsxWorksheet, sheet string, row, row2 int) error {
return f.duplicateConditionalFormat(ws, sheet, row, row2)
},
func(f *File, ws *xlsxWorksheet, sheet string, row, row2 int) error {
return f.duplicateDataValidations(ws, sheet, row, row2)
},
func(f *File, ws *xlsxWorksheet, sheet string, row, row2 int) error {
return f.duplicateMergeCells(ws, sheet, row, row2)
},
}
// GetRows return all the rows in a sheet by given worksheet name, returned as
// a two-dimensional array, where the value of the cell is converted to the
// string type. If the cell format can be applied to the value of the cell,
......@@ -618,7 +632,7 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
}
if row2 < 1 || row == row2 {
return nil
return err
}
var ok bool
......@@ -637,7 +651,7 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
}
if !ok {
return nil
return err
}
idx2 := -1
......@@ -647,10 +661,6 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
break
}
}
if idx2 == -1 && len(ws.SheetData.Row) >= row2 {
return nil
}
rowCopy.C = append(make([]xlsxC, 0, len(rowCopy.C)), rowCopy.C...)
rowCopy.adjustSingleRowDimensions(row2 - row)
_ = f.adjustSingleRowFormulas(sheet, sheet, &rowCopy, row, row2-row, true)
......@@ -660,12 +670,76 @@ func (f *File) DuplicateRowTo(sheet string, row, row2 int) error {
} else {
ws.SheetData.Row = append(ws.SheetData.Row, rowCopy)
}
return f.duplicateMergeCells(sheet, ws, row, row2)
for _, fn := range duplicateHelperFunc {
if err := fn(f, ws, sheet, row, row2); err != nil {
return err
}
}
return err
}
// duplicateConditionalFormat create conditional formatting for the destination
// row if there are conditional formats in the copied row.
func (f *File) duplicateConditionalFormat(ws *xlsxWorksheet, sheet string, row, row2 int) error {
var cfs []*xlsxConditionalFormatting
for _, cf := range ws.ConditionalFormatting {
if cf != nil {
if !strings.Contains(cf.SQRef, ":") {
cf.SQRef += ":" + cf.SQRef
}
abs := strings.Contains(cf.SQRef, "$")
coordinates, err := rangeRefToCoordinates(cf.SQRef)
if err != nil {
return err
}
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if y1 == y2 && y1 == row {
cfCopy := deepcopy.Copy(*cf).(xlsxConditionalFormatting)
if cfCopy.SQRef, err = f.coordinatesToRangeRef([]int{x1, row2, x2, row2}, abs); err != nil {
return err
}
cfs = append(cfs, &cfCopy)
}
}
}
ws.ConditionalFormatting = append(ws.ConditionalFormatting, cfs...)
return nil
}
// duplicateDataValidations create data validations for the destination row if
// there are data validation rules in the copied row.
func (f *File) duplicateDataValidations(ws *xlsxWorksheet, sheet string, row, row2 int) error {
if ws.DataValidations == nil {
return nil
}
var dvs []*xlsxDataValidation
for _, dv := range ws.DataValidations.DataValidation {
if dv != nil {
if !strings.Contains(dv.Sqref, ":") {
dv.Sqref += ":" + dv.Sqref
}
abs := strings.Contains(dv.Sqref, "$")
coordinates, err := rangeRefToCoordinates(dv.Sqref)
if err != nil {
return err
}
x1, y1, x2, y2 := coordinates[0], coordinates[1], coordinates[2], coordinates[3]
if y1 == y2 && y1 == row {
dvCopy := deepcopy.Copy(*dv).(xlsxDataValidation)
if dvCopy.Sqref, err = f.coordinatesToRangeRef([]int{x1, row2, x2, row2}, abs); err != nil {
return err
}
dvs = append(dvs, &dvCopy)
}
}
}
ws.DataValidations.DataValidation = append(ws.DataValidations.DataValidation, dvs...)
return nil
}
// duplicateMergeCells merge cells in the destination row if there are single
// row merged cells in the copied row.
func (f *File) duplicateMergeCells(sheet string, ws *xlsxWorksheet, row, row2 int) error {
func (f *File) duplicateMergeCells(ws *xlsxWorksheet, sheet string, row, row2 int) error {
if ws.MergeCells == nil {
return nil
}
......
......@@ -878,6 +878,23 @@ func TestDuplicateRow(t *testing.T) {
}))
assert.NoError(t, f.SetCellFormula("Sheet1", "A1", "Amount+C1"))
assert.NoError(t, f.SetCellValue("Sheet1", "A10", "A10"))
format, err := f.NewConditionalStyle(&Style{Font: &Font{Color: "9A0511"}, Fill: Fill{Type: "pattern", Color: []string{"FEC7CE"}, Pattern: 1}})
assert.NoError(t, err)
expected := []ConditionalFormatOptions{
{Type: "cell", Criteria: "greater than", Format: format, Value: "0"},
}
assert.NoError(t, f.SetConditionalFormat("Sheet1", "A1", expected))
dv := NewDataValidation(true)
dv.Sqref = "A1"
assert.NoError(t, dv.SetDropList([]string{"1", "2", "3"}))
assert.NoError(t, f.AddDataValidation("Sheet1", dv))
ws, ok := f.Sheet.Load("xl/worksheets/sheet1.xml")
assert.True(t, ok)
ws.(*xlsxWorksheet).DataValidations.DataValidation[0].Sqref = "A1"
assert.NoError(t, f.DuplicateRowTo("Sheet1", 1, 10))
formula, err := f.GetCellFormula("Sheet1", "A10")
assert.NoError(t, err)
......@@ -885,6 +902,28 @@ func TestDuplicateRow(t *testing.T) {
value, err := f.GetCellValue("Sheet1", "A11")
assert.NoError(t, err)
assert.Equal(t, "A10", value)
cfs, err := f.GetConditionalFormats("Sheet1")
assert.NoError(t, err)
assert.Len(t, cfs, 2)
assert.Equal(t, expected, cfs["A10:A10"])
dvs, err := f.GetDataValidations("Sheet1")
assert.NoError(t, err)
assert.Len(t, dvs, 2)
assert.Equal(t, "A10:A10", dvs[1].Sqref)
// Test duplicate data validation with row number exceeds maximum limit
assert.Equal(t, ErrMaxRows, f.duplicateDataValidations(ws.(*xlsxWorksheet), "Sheet1", 1, TotalRows+1))
// Test duplicate data validation with invalid range reference
ws.(*xlsxWorksheet).DataValidations.DataValidation[0].Sqref = "A"
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), f.duplicateDataValidations(ws.(*xlsxWorksheet), "Sheet1", 1, 10))
// Test duplicate conditional formatting with row number exceeds maximum limit
assert.Equal(t, ErrMaxRows, f.duplicateConditionalFormat(ws.(*xlsxWorksheet), "Sheet1", 1, TotalRows+1))
// Test duplicate conditional formatting with invalid range reference
ws.(*xlsxWorksheet).ConditionalFormatting[0].SQRef = "A"
assert.Equal(t, newCellNameToCoordinatesError("A", newInvalidCellNameError("A")), f.duplicateConditionalFormat(ws.(*xlsxWorksheet), "Sheet1", 1, 10))
}
func TestDuplicateRowTo(t *testing.T) {
......@@ -911,9 +950,9 @@ func TestDuplicateMergeCells(t *testing.T) {
ws := &xlsxWorksheet{MergeCells: &xlsxMergeCells{
Cells: []*xlsxMergeCell{{Ref: "A1:-"}},
}}
assert.EqualError(t, f.duplicateMergeCells("Sheet1", ws, 0, 0), `cannot convert cell "-" to coordinates: invalid cell name "-"`)
assert.EqualError(t, f.duplicateMergeCells(ws, "Sheet1", 0, 0), `cannot convert cell "-" to coordinates: invalid cell name "-"`)
ws.MergeCells.Cells[0].Ref = "A1:B1"
assert.EqualError(t, f.duplicateMergeCells("SheetN", ws, 1, 2), "sheet SheetN does not exist")
assert.EqualError(t, f.duplicateMergeCells(ws, "SheetN", 1, 2), "sheet SheetN does not exist")
}
func TestGetValueFromInlineStr(t *testing.T) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册