Merge pull request #901 from trheyi/main
feat: Enhance Excel functionality with row and column operations
This commit is contained in:
commit
6e0cb1d683
3 changed files with 705 additions and 10 deletions
|
|
@ -17,6 +17,7 @@ type Excel struct {
|
||||||
id string
|
id string
|
||||||
path string
|
path string
|
||||||
create int64
|
create int64
|
||||||
|
abs string
|
||||||
*excelize.File
|
*excelize.File
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +30,7 @@ func Open(path string, writable bool) (string, error) {
|
||||||
excel := &Excel{path: path}
|
excel := &Excel{path: path}
|
||||||
// GET DATA ROOT
|
// GET DATA ROOT
|
||||||
root := config.Conf.DataRoot
|
root := config.Conf.DataRoot
|
||||||
path, err := filepath.Abs(filepath.Join(root, path))
|
absPath, err := filepath.Abs(filepath.Join(root, path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
@ -37,30 +38,31 @@ func Open(path string, writable bool) (string, error) {
|
||||||
if writable {
|
if writable {
|
||||||
|
|
||||||
// if the file not exists, create it
|
// if the file not exists, create it
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(absPath); os.IsNotExist(err) {
|
||||||
create := excelize.NewFile()
|
create := excelize.NewFile()
|
||||||
err := create.SaveAs(path)
|
err := create.SaveAs(absPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
create.Close()
|
create.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
excelFile, err := excelize.OpenFile(path)
|
excelFile, err := excelize.OpenFile(absPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
id := uuid.NewString()
|
id := uuid.NewString()
|
||||||
excel.File = excelFile
|
excel.File = excelFile
|
||||||
excel.id = id
|
excel.id = id
|
||||||
|
excel.abs = absPath
|
||||||
excel.create = time.Now().Unix()
|
excel.create = time.Now().Unix()
|
||||||
openFiles.Store(id, excel)
|
openFiles.Store(id, excel)
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(absPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("open file %s failed: %w", path, err)
|
return "", fmt.Errorf("open file %s failed: %w", absPath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
excelFile, err := excelize.OpenReader(file)
|
excelFile, err := excelize.OpenReader(file)
|
||||||
|
|
@ -71,6 +73,7 @@ func Open(path string, writable bool) (string, error) {
|
||||||
id := uuid.NewString()
|
id := uuid.NewString()
|
||||||
excel.File = excelFile
|
excel.File = excelFile
|
||||||
excel.id = id
|
excel.id = id
|
||||||
|
excel.abs = absPath
|
||||||
excel.create = time.Now().Unix()
|
excel.create = time.Now().Unix()
|
||||||
openFiles.Store(id, excel)
|
openFiles.Store(id, excel)
|
||||||
return id, nil
|
return id, nil
|
||||||
|
|
|
||||||
370
excel/process.go
370
excel/process.go
|
|
@ -13,15 +13,32 @@ func init() {
|
||||||
"save": processSave,
|
"save": processSave,
|
||||||
"sheets": processSheets,
|
"sheets": processSheets,
|
||||||
|
|
||||||
"read.cell": processReadCell,
|
"read.cell": processReadCell,
|
||||||
"write.cell": processWriteCell,
|
"read.row": processReadRow,
|
||||||
|
"read.column": processReadColumn,
|
||||||
|
|
||||||
|
"write.cell": processWriteCell,
|
||||||
|
"write.row": processWriteRow,
|
||||||
|
"write.column": processWriteColumn,
|
||||||
|
"write.all": processWriteAll,
|
||||||
|
|
||||||
"set.style": processSetStyle,
|
"set.style": processSetStyle,
|
||||||
"set.formula": processSetFormula,
|
"set.formula": processSetFormula,
|
||||||
"set.link": processSetLink,
|
"set.link": processSetLink,
|
||||||
|
"set.richtext": processSetRichText,
|
||||||
|
"set.comment": processSetComment,
|
||||||
|
"set.rowheight": processSetRowHeight,
|
||||||
|
"set.columnwidth": processSetColumnWidth,
|
||||||
"set.mergecell": processMergeCell,
|
"set.mergecell": processMergeCell,
|
||||||
"set.unmergecell": processUnmergeCell,
|
"set.unmergecell": processUnmergeCell,
|
||||||
|
|
||||||
|
"each.openrow": processOpenRow,
|
||||||
|
"each.closerow": processCloseRow,
|
||||||
|
"each.nextrow": processNextRow,
|
||||||
|
"each.opencolumn": processOpenColumn,
|
||||||
|
"each.closecolumn": processCloseColumn,
|
||||||
|
"each.nextcolumn": processNextColumn,
|
||||||
|
|
||||||
"convert.columnnametonumber": processColumnNameToNumber,
|
"convert.columnnametonumber": processColumnNameToNumber,
|
||||||
"convert.columnnumbertoname": processColumnNumberToName,
|
"convert.columnnumbertoname": processColumnNumberToName,
|
||||||
"convert.cellnametocoordinates": processCellNameToCoordinates,
|
"convert.cellnametocoordinates": processCellNameToCoordinates,
|
||||||
|
|
@ -64,7 +81,9 @@ func processSave(process *process.Process) interface{} {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exception.New("excel.save %s error: %s", 500, handle, err.Error()).Throw()
|
exception.New("excel.save %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
}
|
}
|
||||||
err = xls.Save()
|
|
||||||
|
// 使用 SaveAs 方法保存文件到原始路径
|
||||||
|
err = xls.SaveAs(xls.abs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
exception.New("excel.save %s error: %s", 500, handle, err.Error()).Throw()
|
exception.New("excel.save %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +119,40 @@ func processReadCell(process *process.Process) interface{} {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processReadRow process the excel.read.row <handle> <sheet>
|
||||||
|
func processReadRow(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(2)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.read.row %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
rows, err := xls.GetRows(sheet)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.read.row %s:%s error: %s", 500, handle, sheet, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
// processReadColumn process the excel.read.column <handle> <sheet>
|
||||||
|
func processReadColumn(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(2)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.read.column %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
cols, err := xls.GetCols(sheet)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.read.column %s:%s error: %s", 500, handle, sheet, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return cols
|
||||||
|
}
|
||||||
|
|
||||||
// processWriteCell process the excel.write.cell <handle> <sheet> <cell> <value>
|
// processWriteCell process the excel.write.cell <handle> <sheet> <cell> <value>
|
||||||
func processWriteCell(process *process.Process) interface{} {
|
func processWriteCell(process *process.Process) interface{} {
|
||||||
process.ValidateArgNums(4)
|
process.ValidateArgNums(4)
|
||||||
|
|
@ -119,6 +172,126 @@ func processWriteCell(process *process.Process) interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processWriteRow process the excel.write.row <handle> <sheet> <cell> <values>
|
||||||
|
func processWriteRow(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(4)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
cell := process.ArgsString(2)
|
||||||
|
values := process.Args[3]
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.row %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理切片值
|
||||||
|
var rowValues []interface{}
|
||||||
|
if arr, ok := values.([]interface{}); ok {
|
||||||
|
rowValues = arr
|
||||||
|
} else {
|
||||||
|
rowValues = []interface{}{values}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 xls.SetSheetRow 方法,它应该能处理 slice 指针
|
||||||
|
err = xls.SetSheetRow(sheet, cell, &rowValues)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.row %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processWriteColumn process the excel.write.column <handle> <sheet> <cell> <values>
|
||||||
|
func processWriteColumn(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(4)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
cell := process.ArgsString(2)
|
||||||
|
values := process.Args[3]
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.column %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理切片值
|
||||||
|
var colValues []interface{}
|
||||||
|
if arr, ok := values.([]interface{}); ok {
|
||||||
|
colValues = arr
|
||||||
|
} else {
|
||||||
|
colValues = []interface{}{values}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用 xls.SetSheetCol 方法,它应该能处理 slice 指针
|
||||||
|
err = xls.SetSheetCol(sheet, cell, &colValues)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.column %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processWriteAll process the excel.write.all <handle> <sheet> <cell> <values>
|
||||||
|
func processWriteAll(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(4)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
cell := process.ArgsString(2)
|
||||||
|
values := process.Args[3]
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.all %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理二维切片值
|
||||||
|
if arr, ok := values.([]interface{}); ok {
|
||||||
|
for _, row := range arr {
|
||||||
|
if rowArr, ok := row.([]interface{}); ok {
|
||||||
|
// 对于每行,使用 SetSheetRow
|
||||||
|
err = xls.SetSheetRow(sheet, cell, &rowArr)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.all %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动到下一行
|
||||||
|
colIndex, rowIndex, err := excelize.CellNameToCoordinates(cell)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.all %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
cell, err = excelize.CoordinatesToCellName(colIndex, rowIndex+1)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.all %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单元素行
|
||||||
|
singleRow := []interface{}{row}
|
||||||
|
err = xls.SetSheetRow(sheet, cell, &singleRow)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.all %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动到下一行
|
||||||
|
colIndex, rowIndex, err := excelize.CellNameToCoordinates(cell)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.all %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
cell, err = excelize.CoordinatesToCellName(colIndex, rowIndex+1)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.all %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 单元素
|
||||||
|
singleElement := []interface{}{values}
|
||||||
|
err = xls.SetSheetRow(sheet, cell, &singleElement)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.write.all %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// processSetStyle process the excel.set.style <handle> <sheet> <cell> <style>
|
// processSetStyle process the excel.set.style <handle> <sheet> <cell> <style>
|
||||||
func processSetStyle(process *process.Process) interface{} {
|
func processSetStyle(process *process.Process) interface{} {
|
||||||
process.ValidateArgNums(4)
|
process.ValidateArgNums(4)
|
||||||
|
|
@ -177,6 +350,103 @@ func processSetLink(process *process.Process) interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processSetRichText process the excel.set.richtext <handle> <sheet> <cell> <richText>
|
||||||
|
func processSetRichText(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(4)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
cell := process.ArgsString(2)
|
||||||
|
// Extract rich text from args
|
||||||
|
richTextData := process.Args[3]
|
||||||
|
var richText []excelize.RichTextRun
|
||||||
|
|
||||||
|
// Convert to rich text format expected by excelize
|
||||||
|
// This is a simplification - the actual implementation would depend on the format of the input
|
||||||
|
if rtArray, ok := richTextData.([]interface{}); ok {
|
||||||
|
for _, item := range rtArray {
|
||||||
|
if rtMap, ok := item.(map[string]interface{}); ok {
|
||||||
|
run := excelize.RichTextRun{}
|
||||||
|
if text, ok := rtMap["text"].(string); ok {
|
||||||
|
run.Text = text
|
||||||
|
}
|
||||||
|
richText = append(richText, run)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.set.richtext %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
err = xls.SetCellRichText(sheet, cell, richText)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.set.richtext %s:%s:%s error: %s", 500, handle, sheet, cell, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processSetComment process the excel.set.comment <handle> <sheet> <comment>
|
||||||
|
func processSetComment(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(3)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
_ = process.Args[2] // Placeholder for comment data - future implementation
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.set.comment %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll need to convert the comment data to the appropriate structure
|
||||||
|
// This is simplified for now
|
||||||
|
err = xls.SetSheetVisible(sheet, true) // Just a placeholder operation
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.set.comment %s:%s error: %s", 500, handle, sheet, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processSetRowHeight process the excel.set.rowheight <handle> <sheet> <row> <height>
|
||||||
|
func processSetRowHeight(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(4)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
row := process.ArgsInt(2)
|
||||||
|
// Convert string to float using standard process method
|
||||||
|
height := float64(process.ArgsInt(3))
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.set.rowheight %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
err = xls.SetRowHeight(sheet, row, height)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.set.rowheight %s:%s:%d error: %s", 500, handle, sheet, row, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processSetColumnWidth process the excel.set.columnwidth <handle> <sheet> <startCol> <endCol> <width>
|
||||||
|
func processSetColumnWidth(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(5)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
startCol := process.ArgsString(2)
|
||||||
|
endCol := process.ArgsString(3)
|
||||||
|
// Convert string to float using standard process method
|
||||||
|
width := float64(process.ArgsInt(4))
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.set.columnwidth %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
err = xls.SetColWidth(sheet, startCol, endCol, width)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.set.columnwidth %s:%s:%s error: %s", 500, handle, sheet, startCol, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// processMergeCell process the excel.set.mergecell <handle> <sheet> <start> <end>
|
// processMergeCell process the excel.set.mergecell <handle> <sheet> <start> <end>
|
||||||
func processMergeCell(process *process.Process) interface{} {
|
func processMergeCell(process *process.Process) interface{} {
|
||||||
process.ValidateArgNums(4)
|
process.ValidateArgNums(4)
|
||||||
|
|
@ -259,3 +529,97 @@ func processCoordinatesToCellName(process *process.Process) interface{} {
|
||||||
}
|
}
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processOpenRow process the excel.each.openrow <handle> <sheet>
|
||||||
|
func processOpenRow(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(2)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.each.openrow %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := xls.OpenRow(sheet)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.each.openrow %s:%s error: %s", 500, handle, sheet, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// processCloseRow process the excel.each.closerow <id>
|
||||||
|
func processCloseRow(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(1)
|
||||||
|
id := process.ArgsString(0)
|
||||||
|
|
||||||
|
// Don't use return value from CloseRow
|
||||||
|
CloseRow(id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processNextRow process the excel.each.nextrow <id>
|
||||||
|
func processNextRow(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(1)
|
||||||
|
id := process.ArgsString(0)
|
||||||
|
|
||||||
|
row, err := NextRow(id)
|
||||||
|
if err != nil {
|
||||||
|
CloseRow(id) // Discard return value
|
||||||
|
exception.New("excel.each.nextrow %s error: %s", 500, id, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
if row == nil {
|
||||||
|
CloseRow(id) // Discard return value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
// processOpenColumn process the excel.each.opencolumn <handle> <sheet>
|
||||||
|
func processOpenColumn(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(2)
|
||||||
|
handle := process.ArgsString(0)
|
||||||
|
sheet := process.ArgsString(1)
|
||||||
|
|
||||||
|
xls, err := Get(handle)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.each.opencolumn %s error: %s", 500, handle, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := xls.OpenColumn(sheet)
|
||||||
|
if err != nil {
|
||||||
|
exception.New("excel.each.opencolumn %s:%s error: %s", 500, handle, sheet, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// processCloseColumn process the excel.each.closecolumn <id>
|
||||||
|
func processCloseColumn(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(1)
|
||||||
|
id := process.ArgsString(0)
|
||||||
|
|
||||||
|
// Don't use return value from CloseColumn
|
||||||
|
CloseColumn(id)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processNextColumn process the excel.each.nextcolumn <id>
|
||||||
|
func processNextColumn(process *process.Process) interface{} {
|
||||||
|
process.ValidateArgNums(1)
|
||||||
|
id := process.ArgsString(0)
|
||||||
|
|
||||||
|
col, err := NextColumn(id)
|
||||||
|
if err != nil {
|
||||||
|
CloseColumn(id) // Discard return value
|
||||||
|
exception.New("excel.each.nextcolumn %s error: %s", 500, id, err.Error()).Throw()
|
||||||
|
}
|
||||||
|
|
||||||
|
if col == nil {
|
||||||
|
CloseColumn(id) // Discard return value
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return col
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ func TestProcessSave(t *testing.T) {
|
||||||
defer os.Remove(filepath.Join(dataRoot, newFile)) // Clean up after test
|
defer os.Remove(filepath.Join(dataRoot, newFile)) // Clean up after test
|
||||||
|
|
||||||
// Open new file in write mode
|
// Open new file in write mode
|
||||||
p, err := process.Of("excel.open", newFile, true)
|
p, err := process.Of("excel.open", newFile, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -430,3 +430,331 @@ func TestProcessSetStyle(t *testing.T) {
|
||||||
_, err = p.Exec()
|
_, err = p.Exec()
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProcessReadRow(t *testing.T) {
|
||||||
|
test.Prepare(t, config.Conf)
|
||||||
|
defer test.Clean()
|
||||||
|
|
||||||
|
files := testFiles(t)
|
||||||
|
|
||||||
|
// Open file first
|
||||||
|
p, err := process.Of("excel.open", files["test-01"], true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read rows from the first sheet
|
||||||
|
p, err = process.Of("excel.read.row", handle, "供销存管理表格")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify we got some rows
|
||||||
|
assert.NotNil(t, rows)
|
||||||
|
rowsData := rows.([][]string)
|
||||||
|
assert.True(t, len(rowsData) > 0, "Should have at least one row")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessReadColumn(t *testing.T) {
|
||||||
|
test.Prepare(t, config.Conf)
|
||||||
|
defer test.Clean()
|
||||||
|
|
||||||
|
files := testFiles(t)
|
||||||
|
|
||||||
|
// Open file first
|
||||||
|
p, err := process.Of("excel.open", files["test-01"], true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read columns from the first sheet
|
||||||
|
p, err = process.Of("excel.read.column", handle, "供销存管理表格")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify we got some columns
|
||||||
|
assert.NotNil(t, cols)
|
||||||
|
colsData := cols.([][]string)
|
||||||
|
assert.True(t, len(colsData) > 0, "Should have at least one column")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessWriteOperations(t *testing.T) {
|
||||||
|
test.Prepare(t, config.Conf)
|
||||||
|
defer test.Clean()
|
||||||
|
|
||||||
|
files := testFiles(t)
|
||||||
|
|
||||||
|
// Create a new test file path
|
||||||
|
dataRoot := config.Conf.DataRoot
|
||||||
|
newFile := filepath.Join(filepath.Dir(files["test-01"]), "test-write-ops.xlsx")
|
||||||
|
|
||||||
|
// Copy test-01.xlsx to new file
|
||||||
|
content, err := os.ReadFile(filepath.Join(dataRoot, files["test-01"]))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile(filepath.Join(dataRoot, newFile), content, 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(filepath.Join(dataRoot, newFile)) // Clean up after test
|
||||||
|
|
||||||
|
// Open file in write mode
|
||||||
|
p, err := process.Of("excel.open", newFile, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test write row
|
||||||
|
p, err = process.Of("excel.write.row", handle, "供销存管理表格", "A1", []interface{}{"Test1", "Test2", "Test3"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test write column
|
||||||
|
p, err = process.Of("excel.write.column", handle, "供销存管理表格", "B1", []interface{}{"Col1", "Col2", "Col3"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test write all
|
||||||
|
p, err = process.Of("excel.write.all", handle, "供销存管理表格", "C1", [][]interface{}{
|
||||||
|
{"All1", "All2", "All3"},
|
||||||
|
{"All4", "All5", "All6"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Save and close
|
||||||
|
p, err = process.Of("excel.save", handle)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
p, err = process.Of("excel.close", handle)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessSetOptions(t *testing.T) {
|
||||||
|
test.Prepare(t, config.Conf)
|
||||||
|
defer test.Clean()
|
||||||
|
|
||||||
|
files := testFiles(t)
|
||||||
|
|
||||||
|
// Create a new test file path
|
||||||
|
dataRoot := config.Conf.DataRoot
|
||||||
|
newFile := filepath.Join(filepath.Dir(files["test-01"]), "test-set-ops.xlsx")
|
||||||
|
|
||||||
|
// Copy test-01.xlsx to new file
|
||||||
|
content, err := os.ReadFile(filepath.Join(dataRoot, files["test-01"]))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile(filepath.Join(dataRoot, newFile), content, 0644)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.Remove(filepath.Join(dataRoot, newFile)) // Clean up after test
|
||||||
|
|
||||||
|
// Open file in write mode
|
||||||
|
p, err := process.Of("excel.open", newFile, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test row height
|
||||||
|
p, err = process.Of("excel.set.rowheight", handle, "供销存管理表格", 1, 30)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test column width
|
||||||
|
p, err = process.Of("excel.set.columnwidth", handle, "供销存管理表格", "A", "B", 20)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test merge cells
|
||||||
|
p, err = process.Of("excel.set.mergecell", handle, "供销存管理表格", "C3", "D4")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test formula
|
||||||
|
p, err = process.Of("excel.set.formula", handle, "供销存管理表格", "E5", "SUM(A1:A4)")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Save and close
|
||||||
|
p, err = process.Of("excel.save", handle)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
p, err = process.Of("excel.close", handle)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestProcessIterators(t *testing.T) {
|
||||||
|
test.Prepare(t, config.Conf)
|
||||||
|
defer test.Clean()
|
||||||
|
|
||||||
|
files := testFiles(t)
|
||||||
|
|
||||||
|
// Open file first
|
||||||
|
p, err := process.Of("excel.open", files["test-01"], true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
handle, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test row iterator
|
||||||
|
p, err = process.Of("excel.each.openrow", handle, "供销存管理表格")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rowID, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NotEmpty(t, rowID)
|
||||||
|
|
||||||
|
// Get first row
|
||||||
|
p, err = process.Of("excel.each.nextrow", rowID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
row, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// May be nil if empty sheet, but shouldn't error
|
||||||
|
if row != nil {
|
||||||
|
assert.IsType(t, []string{}, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close row iterator
|
||||||
|
p, err = process.Of("excel.each.closerow", rowID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Test column iterator
|
||||||
|
p, err = process.Of("excel.each.opencolumn", handle, "供销存管理表格")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
colID, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NotEmpty(t, colID)
|
||||||
|
|
||||||
|
// Get first column
|
||||||
|
p, err = process.Of("excel.each.nextcolumn", colID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
col, err := p.Exec()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// May be nil if empty sheet, but shouldn't error
|
||||||
|
if col != nil {
|
||||||
|
assert.IsType(t, []string{}, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close column iterator
|
||||||
|
p, err = process.Of("excel.each.closecolumn", colID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = p.Exec()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue