package bbxt import ( "fmt" "reflect" "sort" "github.com/go-kratos/kratos/v2/log" "github.com/xuri/excelize/v2" ) // 最简单的通用函数 func (b *BbxtTools) SimpleFillExcel(templatePath, outputPath string, dataSlice interface{}) error { // 1. 打开模板 f, err := excelize.OpenFile(templatePath) if err != nil { return err } defer f.Close() sheet := f.GetSheetName(0) // 1.1 获取第二行模板样式 resellerTplRow := 2 styleIDReseller, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", resellerTplRow)) if err != nil { log.Errorf("获取分销商总计样式失败: %v", err) styleIDReseller = 0 } // 1.2 获取分销商总计行高 rowHeightReseller, err := f.GetRowHeight(sheet, resellerTplRow) if err != nil { log.Errorf("获取分销商总计行高失败: %v", err) rowHeightReseller = 31 // 默认高度 } // 2. 反射获取切片数据 v := reflect.ValueOf(dataSlice) if v.Kind() != reflect.Slice { return fmt.Errorf("dataSlice must be a slice") } // 3. 从第2行开始填充 row := 2 for i := 0; i < v.Len(); i++ { item := v.Index(i).Interface() currentRow := row + i // 4. 将item转换为一行数据 var rowData []interface{} // 如果是切片 if reflect.TypeOf(item).Kind() == reflect.Slice { itemV := reflect.ValueOf(item) for j := 0; j < itemV.Len(); j++ { rowData = append(rowData, itemV.Index(j).Interface()) } } else if reflect.TypeOf(item).Kind() == reflect.Struct { itemV := reflect.ValueOf(item) for j := 0; j < itemV.NumField(); j++ { if itemV.Field(j).CanInterface() { rowData = append(rowData, itemV.Field(j).Interface()) } } } else { rowData = []interface{}{item} } // 4.1 设置行高 f.SetRowHeight(sheet, currentRow, rowHeightReseller) // 5. 填充到Excel for col, value := range rowData { cell := fmt.Sprintf("%c%d", 'A'+col, currentRow) f.SetCellValue(sheet, cell, value) } // 5.1 使用第二行模板样式 if styleIDReseller != 0 { f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow), styleIDReseller) } } // 6. 保存 return f.SaveAs(outputPath) } // 分销商负利润详情填充excel // 1.使用模板文件作为输出文件 // 2.分销商总计使用第二行样式(宽高、背景、颜色等) // 3.商品详情使用第三行样式(宽高、背景、颜色等) // 4.保存为新文件 func (b *BbxtTools) resellerDetailFillExcel(templatePath, outputPath string, dataSlice []*ResellerLoss) error { // 1. 读取模板 f, err := excelize.OpenFile(templatePath) if err != nil { return err } defer f.Close() sheet := f.GetSheetName(0) // 获取模板样式1:第二行-分销商总计 resellerTplRow := 2 styleIDReseller, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", resellerTplRow)) if err != nil { log.Errorf("获取分销商总计样式失败: %v", err) styleIDReseller = 0 } rowHeightReseller, err := f.GetRowHeight(sheet, resellerTplRow) if err != nil { log.Errorf("获取分销商总计行高失败: %v", err) rowHeightReseller = 31 // 默认高度 } // 获取模板样式2:第三行-产品亏损明细 productTplRow := 3 styleIDProduct, err := f.GetCellStyle(sheet, fmt.Sprintf("A%d", productTplRow)) if err != nil { log.Errorf("获取商品详情样式失败: %v", err) styleIDProduct = 0 } rowHeightProduct, err := f.GetRowHeight(sheet, productTplRow) if err != nil { log.Errorf("获取商品详情行高失败: %v", err) rowHeightProduct = 25 // 默认高度 } currentRow := 2 for _, reseller := range dataSlice { // 3. 填充经销商数据 (ResellerName, Total) // 设置行高 f.SetRowHeight(sheet, currentRow, rowHeightReseller) // 设置单元格值 f.SetCellValue(sheet, fmt.Sprintf("A%d", currentRow), reseller.ResellerName) f.SetCellValue(sheet, fmt.Sprintf("B%d", currentRow), reseller.Total) // 应用样式 if styleIDReseller != 0 { f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow), styleIDReseller) } currentRow++ // 4. 填充产品亏损明细 // 先对 ProductLoss 进行排序 var products []ProductLoss for _, p := range reseller.ProductLoss { products = append(products, p) } // 按 Loss 升序排序 (亏损越多越靠前,负数越小) sort.Slice(products, func(i, j int) bool { return products[i].Loss < products[j].Loss }) for _, p := range products { // 设置行高 f.SetRowHeight(sheet, currentRow, rowHeightProduct) // 设置单元格值 f.SetCellValue(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("·%s", p.ProductName)) f.SetCellValue(sheet, fmt.Sprintf("B%d", currentRow), p.Loss) // 应用样式 if styleIDProduct != 0 { f.SetCellStyle(sheet, fmt.Sprintf("A%d", currentRow), fmt.Sprintf("B%d", currentRow), styleIDProduct) } currentRow++ } } // buffer, err := f.WriteToBuffer() // if err != nil { // return err // } // return buffer.Bytes(), nil // 6. 保存 return f.SaveAs(outputPath) } // excel2picPy 将excel转换为图片python // python 接口如下: // curl --location --request POST 'http://192.168.6.109:8010/api/v1/convert' \ // --header 'Content-Type: multipart/form-data; boundary=--------------------------952147881043913664015069' \ // --form 'file=@"C:\\Users\\Administrator\\Downloads\\销售同比分析2025-12-29 0-12点.xlsx"' \ // --form 'sheet_name="销售同比分析"' func (b *BbxtTools) excel2picPy(templatePath string, excelBytes []byte) ([]byte, error) { return nil, nil // return picBytes, nil }