package services import ( "baliance.com/gooxml/color" "baliance.com/gooxml/document" "baliance.com/gooxml/measurement" "baliance.com/gooxml/schema/soo/ofc/sharedTypes" "baliance.com/gooxml/schema/soo/wml" "bytes" "encoding/json" "errors" "fmt" wkhtml "github.com/SebastiaanKlippert/go-wkhtmltopdf" "github.com/shopspring/decimal" contractReq "hongze/hongze_mobile_admin/models/request/contract" "hongze/hongze_mobile_admin/models/tables/contract" "hongze/hongze_mobile_admin/models/tables/contract_service_detail" "hongze/hongze_mobile_admin/models/tables/contract_service_template" "hongze/hongze_mobile_admin/models/tables/contract_template" "hongze/hongze_mobile_admin/utils" "html/template" "reflect" "strconv" "strings" ) type TableData struct { List []TableRow `json:"table";description:"列数据"` } type TableRow struct { RowList []TableCel `json:"row";description:"列数据"` } type TableCel struct { Value string `json:"value";description:"展示的数据"` ColumnSpan int `json:"column_span";description:"需要合同的列数量"` RowSpan int `json:"row_span";description:"需要合同的行数量"` IsMerged bool `json:"is_merged";description:"是否需要上下行合并"` IsFirstMerged bool `json:"is_first_merged";description:"是否是第一次合并上下行"` Background string `json:"background";description:"背景色"` IsBold bool `json:"is_bold";description:"是否加粗显示"` TextAlign string `json:"text_align";description:"对齐方式"` FontSize float64 `json:"font_size";description:"字体大小"` WidthPercent float64 `json:"width_percent";description:"单元格宽度占整个表格的百分比"` } //获取颜色配置 func getColorConf(background string) (foreground color.Color) { switch background { case "slate_gray": //石板灰 foreground = color.SlateGray case "light_slate_gray": //浅石板灰 foreground = color.LightSlateGray case "light_gray": //浅灰 foreground = color.LightGray case "gray": //灰色 foreground = color.Gray case "gray_1": //灰色_1(中浅灰) foreground = color.RGB(uint8(215), uint8(215), uint8(215)) case "gray_2": //灰色_2(浅灰) foreground = color.RGB(uint8(241), uint8(241), uint8(241)) case "dim_gray": //暗灰色 foreground = color.DimGray case "dark_slate_gray": //深灰色 foreground = color.DarkSlateGray default: foreground = color.LightGray } return } func getTextAlignConf(textAlign string) (align wml.ST_Jc) { switch textAlign { case "left": //居左 align = wml.ST_JcLeft case "center": //居中 align = wml.ST_JcCenter case "right": //居右 align = wml.ST_JcRight case "both": // align = wml.ST_JcBoth default: align = wml.ST_JcLeft } return } //生成word func GenerateWord(contractDetail *contract.ContractDetail) (err error) { wordTemplatePath := getWordPath(contractDetail.TemplateId) if wordTemplatePath == "" { err = errors.New("找不到对应的合同模板") return } doc, err := document.Open(wordTemplatePath) if err != nil { fmt.Println("error opening document: %s", err) return } paragraphs := []document.Paragraph{} for _, p := range doc.Paragraphs() { paragraphs = append(paragraphs, p) } // This sample document uses structured document tags, which are not common // except for in document templates. Normally you can just iterate over the // document's paragraphs. for _, sdt := range doc.StructuredDocumentTags() { for _, p := range sdt.Paragraphs() { paragraphs = append(paragraphs, p) } } doc.AddParagraph() for _, p := range paragraphs { for _, r := range p.Runs() { switch r.Text() { case "{{address}}": // ClearContent clears both text and line breaks within a run, // so we need to add the line break back r.ClearContent() address := contractDetail.Province + contractDetail.City + contractDetail.Address r.AddText(address) //r.AddBreak() //para := doc.InsertParagraphBefore(p) //para.AddRun().AddText("Mr.") //para.SetStyle("Name") // Name is a default style in this template file // //para = doc.InsertParagraphAfter(p) //para.AddRun().AddText("III") //para.SetStyle("Name") case "{{postcode}}": r.ClearContent() r.AddText(contractDetail.Postcode) case "{{phone}}": r.ClearContent() r.AddText(contractDetail.Phone) case "{{fax}}": r.ClearContent() r.AddText(contractDetail.Fax) case "{{remark}}": r.ClearContent() remark := contractDetail.Remark if remark == "" { remark = "无" } r.AddText(remark) case "{{start_date}}": r.ClearContent() r.AddText(contractDetail.StartDate.Format("2006 年 01 月 02 日")) case "{{end_date}}": r.ClearContent() r.AddText(contractDetail.EndDate.Format("2006 年 01 月 02 日")) case "{{num_year}}": r.ClearContent() //合同结束日期与合同开始日期的时间差(小时差) newDecimal := decimal.NewFromFloat(contractDetail.EndDate.Sub(contractDetail.StartDate).Hours()) //分母为365天 * 24 小时 newDecimal2 := decimal.NewFromInt(24 * 365) //计算出来相差多少年,保留一位小数(四舍五入) numYearDecimal := newDecimal.Div(newDecimal2).Round(1) //定义最小年份差,不能小于0.1年 minDecimal := decimal.NewFromFloat(0.1) //如果计算出来的年份差小于0.1年,那么该年份差就赋值 0.1年 if numYearDecimal.LessThan(minDecimal) { numYearDecimal = minDecimal } //cnYear, cnErr := utils.ConvertNumToCn(numYearDecimal.String()) //if cnErr != nil { // err = cnErr // return //} r.AddText(numYearDecimal.String()) case "{{price}}": r.ClearContent() priceStr := "" //originalPrice := strconv.FormatFloat(contractDetail.OriginalPrice, 'E', -1, 64) //优惠前金额(小写) newDecimal := decimal.NewFromFloat(contractDetail.OriginalPrice) originalPrice := newDecimal.String() priceStr += "小写:" + originalPrice + "," //优惠前金额(大写) originalCnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.OriginalPrice) if cnyErr != nil { err = cnyErr return } priceStr += "大写:" + originalCnyPrice //如果实际支付金额与订单原金额不符 if contractDetail.OriginalPrice != contractDetail.Price { //优惠后的金额(小写) newDecimal := decimal.NewFromFloat(contractDetail.Price) price := newDecimal.String() priceStr += ",经甲乙双方友好协商,优惠至:" + price + "元," //优惠后的金额(大写) cnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.Price) if cnyErr != nil { err = cnyErr return } priceStr += "大写:" + cnyPrice } r.AddText(priceStr) case "{{pay_remark}}": r.ClearContent() r.AddText(contractDetail.PayRemark) case "{{company_name}}": r.ClearContent() r.AddText(contractDetail.CompanyName) //r.AddBreak() case "{{services}}": r.ClearContent() //赋值当前段落 nowParagraph := p for i := len(contractDetail.Service) - 1; i >= 0; i-- { //表格数据 var tableDataList TableData //表头备注信息 tableTitle := "" item := contractDetail.Service[i] //表格数据 if item.HasDetail == "是" && len(item.DetailList) > 0 { //表格每行数据切片 tableRowList := make([]TableRow, 0) //遍历获取table行数据 for j := 0; j < len(item.DetailList); j++ { //列数据样式初始化 isBold := false backgrandColor := "" fontSize := 10.0 //表头数据样式 if j == 0 { isBold = true backgrandColor = "gray_2" fontSize = 12.0 } //获取每一列的数据 tmpCellList, colErr := getColList(item.DetailList[j]) if colErr != nil { err = colErr return } //定义生成table列数据切片 tableCelList := make([]TableCel, 0) lenCell := len(tmpCellList) for k := 0; k < len(tmpCellList); k++ { //计算出来每一列的宽度占比 start //总宽度 newDecimal := decimal.NewFromFloat(100) //总列数 newDecimal2 := decimal.NewFromInt(int64(lenCell)) //计算出来每一列的宽度占比(四舍五入) widthPercent, _ := newDecimal.Div(newDecimal2).Round(3).Float64() //if !ok { // err = errors.New("word普通数据表格宽度百分比计算失败") // return //} //计算出来每一列的宽度占比 end tableCel := TableCel{ Value: tmpCellList[k], TextAlign: "center", //ColumnSpan int `json:"column_span";description:"需要合同的列数量"` //IsMerged bool `json:"is_merged";description:"是否需要上下行合并"` Background: backgrandColor, IsBold: isBold, FontSize: fontSize, WidthPercent: widthPercent, } tableCelList = append(tableCelList, tableCel) } //将每行数据插入到table行数据切片之中 tableRow := TableRow{ RowList: tableCelList, } tableRowList = append(tableRowList, tableRow) } //赋值table表格数据 tableDataList.List = tableRowList tableTitle = "依照《弘则研究FICC客户服务列表2021》中 小套餐 的服务内容,详细如下:" } else { //获取预设的表格数据 contractServiceTemplate, tmpErr := contract_service_template.GetContractServiceTemplateById(item.ServiceTemplateId) if tmpErr != nil { err = tmpErr return } //赋值table表格数据 jsonStr := contractServiceTemplate.TableValue err = json.Unmarshal([]byte(jsonStr), &tableDataList) if err != nil { return } //表头备注信息 tableTitle = contractServiceTemplate.Remark } //往word中添加表格数据 tmpParagraph, tmpErr := addTable(tableTitle, tableDataList, doc, nowParagraph) if tmpErr != nil { err = tmpErr return } //fmt.Println("nowParagraph:", nowParagraph, "tmpParagraph:", tmpParagraph) //fmt.Println("doc:", doc.Paragraphs()) //fmt.Println("==========:") nowParagraph = tmpParagraph } default: //fmt.Println("not modifying", r.Text()) } } } doc.SaveToFile(fmt.Sprint("./static/word/系统生成合同", contractDetail.ContractId, ".docx")) return } //添加表格数据 func addTable(title string, tableDataList TableData, doc *document.Document, paragraph document.Paragraph) (nowParagraph document.Paragraph, err error) { //fmt.Println("表头名称:", title) //插入一个新的段落 nowParagraph = doc.InsertParagraphBefore(paragraph) nowRun := nowParagraph.AddRun() nowRun.AddBreak() //if title != "" { // fmt.Println("表头名称:", title) // nowRun.Properties().SetSize(11) // nowRun.Properties().SetBold(true) // nowRun.AddText(title) // nowRun.AddBreak() //} //再次插入一个新段落 //_ = doc.InsertParagraphAfter(nowParagraph) //表格数据 { table := doc.InsertTableAfter(nowParagraph) //设置表格宽度 table.Properties().SetWidth(6.5 * measurement.Inch) //表格宽度设置为自动 //table.Properties().SetWidthAuto() //边框 borders := table.Properties().Borders() // thin borders borders.SetAll(wml.ST_BorderSingle, color.Auto, measurement.Zero) //表格数据 rowList := tableDataList.List //每一列合并单元格状态map rowIsMeged := make(map[int]bool) //table.Properties().W for i := 0; i < len(rowList); i++ { //创建新的一行 row := table.AddRow() //设置行高,第二个参数是设置固定值还是自动 row.Properties().SetHeight(30*measurement.Point, wml.ST_HeightRuleAtLeast) //遍历列数据 rowDataList := rowList[i].RowList if rowDataList != nil { for j := 0; j < len(rowDataList); j++ { //当前列是否合并 var isMeged bool isMeged, ok := rowIsMeged[j] if !ok { rowIsMeged[j] = false isMeged = false } cell := row.AddCell() cellPara := cell.AddParagraph() run := cellPara.AddRun() //列数据 cellData := rowDataList[j] //如果合并列大于0,那么就合并列 if cellData.ColumnSpan > 0 { // column span / merged cells cell.Properties().SetColumnSpan(cellData.ColumnSpan) //_ = row.AddCell() } //如果指定了上下单元格合并,那么去合并上下单元格 if cellData.IsMerged { //将当前合并单元格状态调整为true rowIsMeged[j] = true //合并单元格类型 var mergeVal wml.ST_Merge if isMeged { //如果上一层已经是合并了,那么这一层是继续合并 mergeVal = wml.ST_MergeContinue } else { //如果上一层不是合并,那么这一层是开始合并 mergeVal = wml.ST_MergeRestart } cell.Properties().SetVerticalMerge(mergeVal) } else { //将当前合并单元格状态调整为false,这样后续如果再次碰到合并单元格操作,就是重新开始合并了 rowIsMeged[j] = false } //背景色 if cellData.Background != "" { cell.Properties().SetShading(wml.ST_ShdSolid, getColorConf(cellData.Background), color.Auto) } //填充内容(文字)垂直对齐方式 cell.Properties().SetVerticalAlignment(wml.ST_VerticalJcCenter) //将单元格设置为宽度百分比 if cellData.WidthPercent > 0 { cell.Properties().SetWidthPercent(cellData.WidthPercent) } //文字排版(居中、左、右) if cellData.TextAlign != "" { cellPara.Properties().SetAlignment(getTextAlignConf(cellData.TextAlign)) //cellPara.Properties().SetAlignment(wml.ST_JcLeft) } //cell.Properties().SetAli //设置是否加粗 run.Properties().SetBold(cellData.IsBold) //设置字体大小 fontSize := 10.0 if cellData.FontSize > 0 { fontSize = cellData.FontSize } run.Properties().SetSize(measurement.Distance(fontSize * measurement.Point)) //设置段落间的间距 cellPara.Properties().Spacing().SetLineSpacing(measurement.Distance(1.4*fontSize*measurement.Point), wml.ST_LineSpacingRuleAuto) //设置段前间距 cellPara.Properties().Spacing().SetBefore(measurement.Distance(0.9 * fontSize * measurement.Point)) //设置段后间距 cellPara.Properties().Spacing().SetAfter(measurement.Distance(0.5 * fontSize * measurement.Point)) //设置字体 run.Properties().SetFontFamily("宋体") //设置排序 run.Properties().SetVerticalAlignment(sharedTypes.ST_VerticalAlignRunBaseline) //设置显示的文字 if cellData.Value != "" { strSlice := strings.Split(cellData.Value, "
") for s := 0; s < len(strSlice); s++ { if s > 0 { run.AddBreak() } run.AddText(strSlice[s]) } } else { run.AddText("") } } } } } return } //获取生成word的docx模板文件 func getWordPath(templateId int) string { var path string switch templateId { case 1: path = "./static/word/template_1.docx" case 2: path = "./static/word/template_2.docx" } return path } //html转pdf数据样式 type html2pdfData struct { CompanyName string `description:"甲方名称"` ContractCode string `description:"合同编号"` Address string `description:"甲方地址"` Postcode string `description:"甲方邮编"` Phone string `description:"甲方电话"` Fax string `description:"传真"` Remark string `description:"备注"` PayRemark string `description:"支付备注"` StartDate string `description:"合同开始日期"` EndDate string `description:"合同结束日期"` NumYear string `description:"合同有效期"` Price string `description:"支付金额"` TableHtml string `description:"表格数据"` } //获取合同样式预览的html func GetHtmlByContractDetail(contractDetail *contract.ContractDetail, htmlType string) (contractHtml string, err error) { contractTemplate, err := contract_template.GetContractTemplateByTemplateId(contractDetail.TemplateId) if err != nil { return } htmlTpl := contractTemplate.Html if htmlType == "pdf" { htmlTpl = contractTemplate.PdfHtml } myTpl := template.Must(template.New("contract").Parse(htmlTpl)) //地址 address := contractDetail.Province + contractDetail.City + contractDetail.Address data := html2pdfData{ CompanyName: contractDetail.CompanyName, ContractCode: contractDetail.ContractCode, Address: address, Postcode: contractDetail.Postcode, Phone: contractDetail.Phone, Fax: contractDetail.Fax, Remark: contractDetail.Remark, PayRemark: contractDetail.PayRemark, StartDate: contractDetail.StartDate.Format("2006年01月02日"), EndDate: contractDetail.EndDate.Format("2006年01月02日"), } if data.Postcode == "" { data.Postcode = "无" } if data.Fax == "" { data.Fax = "无" } if data.Phone == "" { data.Phone = "无" } if data.PayRemark == "" { data.PayRemark = "无" } if data.Remark == "" { data.Remark = "无" } //合同有效期 { //合同结束日期与合同开始日期的时间差(小时差) newDecimal := decimal.NewFromFloat(contractDetail.EndDate.Sub(contractDetail.StartDate).Hours()) //分母为365天 * 24 小时 newDecimal2 := decimal.NewFromInt(24 * 365) //计算出来相差多少年,保留一位小数(四舍五入) numYearDecimal := newDecimal.Div(newDecimal2).Round(1) //定义最小年份差,不能小于0.1年 minDecimal := decimal.NewFromFloat(0.1) //如果计算出来的年份差小于0.1年,那么该年份差就赋值 0.1年 if numYearDecimal.LessThan(minDecimal) { numYearDecimal = minDecimal } //合同有效期 data.NumYear = numYearDecimal.String() } //合同金额 { priceStr := "" //originalPrice := strconv.FormatFloat(contractDetail.OriginalPrice, 'E', -1, 64) //优惠前金额(小写) newDecimal := decimal.NewFromFloat(contractDetail.OriginalPrice) originalPrice := newDecimal.String() priceStr += "小写:" + originalPrice + "," //优惠前金额(大写) originalCnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.OriginalPrice) if cnyErr != nil { err = cnyErr return } priceStr += "大写:" + originalCnyPrice //如果实际支付金额与订单原金额不符 if contractDetail.OriginalPrice != contractDetail.Price { //优惠后的金额(小写) newDecimal := decimal.NewFromFloat(contractDetail.Price) price := newDecimal.String() priceStr += ",经甲乙双方友好协商,优惠至:" + price + "元," //优惠后的金额(大写) cnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.Price) if cnyErr != nil { err = cnyErr return } priceStr += "大写:" + cnyPrice } data.Price = priceStr } buf := new(bytes.Buffer) //实现了读写方法的可变大小的字节缓冲 tplErr := myTpl.Execute(buf, data) if tplErr != nil { err = tplErr return } contractHtml = buf.String() //服务内容 { tableStr := "" tableDataSlice := make([]TableData, 0) tableTitleSlice := make([]string, 0) title := "" if contractDetail.ProductId == 1 { title = "依照《【弘则研究】FICC客户客户服务列表2021》中 " } else { title = "依照《【弘则研究】私募客户客户服务列表2021》中 " } for i := 0; i < len(contractDetail.Service); i++ { //表格数据 var tableDataList TableData item := contractDetail.Service[i] //表头备注信息 tableTitleSlice = append(tableTitleSlice, item.Title) //表格数据 if item.HasDetail == "是" && len(item.DetailList) > 0 { //表格每行数据切片 tableRowList := make([]TableRow, 0) //遍历获取table行数据 for j := 0; j < len(item.DetailList); j++ { //列数据样式初始化 isBold := false backgrandColor := "" fontSize := 13.0 //表头数据样式 if j == 0 { isBold = true backgrandColor = "gray_2" fontSize = 13.0 } //获取每一列的数据 tmpCellList, colErr := getColList(item.DetailList[j]) if colErr != nil { err = colErr return } //定义生成table列数据切片 tableCelList := make([]TableCel, 0) lenCell := len(tmpCellList) for k := 0; k < len(tmpCellList); k++ { //默认30%的宽度,如果不是第一列,那么需要额外计算 widthPercent := 30.0 if k > 0 { //计算出来每一列的宽度占比 start //总宽度 newDecimal := decimal.NewFromFloat(70) //总列数 newDecimal2 := decimal.NewFromInt(int64(lenCell) - 1) //计算出来每一列的宽度占比(四舍五入) tmpWidthPercent, _ := newDecimal.Div(newDecimal2).Round(3).Float64() //if !ok { // err = errors.New("word普通数据表格宽度百分比计算失败") // return //} widthPercent = tmpWidthPercent //计算出来每一列的宽度占比 end } tableCel := TableCel{ Value: tmpCellList[k], TextAlign: "center", //ColumnSpan int `json:"column_span";description:"需要合同的列数量"` //IsMerged bool `json:"is_merged";description:"是否需要上下行合并"` Background: backgrandColor, IsBold: isBold, FontSize: fontSize, WidthPercent: widthPercent, } tableCelList = append(tableCelList, tableCel) } //将每行数据插入到table行数据切片之中 tableRow := TableRow{ RowList: tableCelList, } tableRowList = append(tableRowList, tableRow) } //赋值table表格数据 tableDataList.List = tableRowList } else { //获取预设的表格数据 contractServiceTemplate, tmpErr := contract_service_template.GetContractServiceTemplateById(item.ServiceTemplateId) if tmpErr != nil { err = tmpErr return } //赋值table表格数据 jsonStr := contractServiceTemplate.TableValue tmpEerr := json.Unmarshal([]byte(jsonStr), &tableDataList) if tmpEerr != nil { err = tmpEerr return } } tableDataSlice = append(tableDataSlice, tableDataList) } titleStr := strings.Join(tableTitleSlice, "、") title += titleStr + "的服务内容,详细如下:" if htmlType == "pdf" { tableStr += `

` + title + `

` } else { tableStr = `

` + title + `

` } for _, tableDataList := range tableDataSlice { //往word中添加表格数据 if htmlType == "pdf" { tableStr += getTableStrByPdf(tableDataList) } else { tableStr += getTableStr(tableDataList) } } data.TableHtml = tableStr } //fmt.Println("TableHtml:", data.TableHtml) contractHtml = strings.Replace(contractHtml, `\{\{\{TableHtml\}\}\}`, data.TableHtml, -1) return //生成pdf //pdfPath := fmt.Sprint("./static/word/系统生成合同", contractDetail.ContractCode, ".pdf") //err = Html2Pdf(contractHtml, pdfPath) //if err != nil { // return //} ////defer func() { //// //删除对应的Pdf //// os.Remove(pdfPath) ////}() // //return } //生成合同服务的预览表格html代码 func getTableStr(tableDataList TableData) (tableStr string) { //如果表格需要分页,那么在table的style里面添加该配置:page-break-inside: avoid !important tableStr += `` rowList := tableDataList.List for i := 0; i < len(rowList); i++ { //创建新的一行 tableStr += `` // //row := table.AddRow() ////设置行高,第二个参数是设置固定值还是自动 //row.Properties().SetHeight(30*measurement.Point, wml.ST_HeightRuleAtLeast) // //遍历列数据 rowDataList := rowList[i].RowList cellStr := "" if rowDataList != nil { for j := 0; j < len(rowDataList); j++ { //当前列是否合并 // // // tdStr := `` } } tableStr += cellStr + `` } tableStr += `
2.市场跟踪类报告2.市场跟踪类报告市场跟踪市场估值 0 { // column span / merged cells cellOtherStr += ` colspan="` + strconv.Itoa(cellData.ColumnSpan) + `" ` } //如果指定了上下单元格合并,那么去合并上下单元格 if cellData.IsMerged { if cellData.IsFirstMerged { cellOtherStr += ` rowspan="` + strconv.Itoa(cellData.RowSpan) + `" ` } else { //如果是合并行,且不是第一行,那么就退出当前单元格循环,进入下一个循环 continue } } //背景色 if cellData.Background != "" { styleStr += `background-color: #F0F2F5;` } //将单元格设置为宽度百分比 if cellData.WidthPercent > 0 { widthDecimal := decimal.NewFromFloat(cellData.WidthPercent) cellOtherStr += ` width="` + widthDecimal.String() + `%" ` } //文字排版(居中、左、右) if cellData.TextAlign != "" { cellOtherStr += ` align="` + cellData.TextAlign + `" ` } //cell.Properties().SetAli //设置是否加粗 if cellData.IsBold { styleStr += `font-weight:bold;` } //设置字体大小 fontSize := 10.0 if cellData.FontSize > 0 { fontSize = cellData.FontSize } fontDecimal := decimal.NewFromFloat(fontSize) styleStr += `font-size: ` + fontDecimal.String() + `pt;` bodyStr := cellData.Value styleStr += `" ` cellStr += tdStr + styleStr + cellOtherStr + `>` + bodyStr + `
` return } //生成合同服务的pdf表格html代码 func getTableStrByPdf(tableDataList TableData) (tableStr string) { //如果表格需要分页,那么在table的style里面添加该配置:page-break-inside: avoid !important tableStr += `` rowList := tableDataList.List for i := 0; i < len(rowList); i++ { //创建新的一行 tableStr += `` // //row := table.AddRow() ////设置行高,第二个参数是设置固定值还是自动 //row.Properties().SetHeight(30*measurement.Point, wml.ST_HeightRuleAtLeast) // //遍历列数据 rowDataList := rowList[i].RowList cellStr := "" if rowDataList != nil { for j := 0; j < len(rowDataList); j++ { //当前列是否合并 // // // tdStr := `` } } tableStr += cellStr + `` } tableStr += `
2.市场跟踪类报告2.市场跟踪类报告市场跟踪市场估值 0 { // column span / merged cells cellOtherStr += ` colspan="` + strconv.Itoa(cellData.ColumnSpan) + `" ` } //如果指定了上下单元格合并,那么去合并上下单元格 if cellData.IsMerged { if cellData.IsFirstMerged { cellOtherStr += ` rowspan="` + strconv.Itoa(cellData.RowSpan) + `" ` } else { //如果是合并行,且不是第一行,那么就退出当前单元格循环,进入下一个循环 continue } } //背景色 if cellData.Background != "" { styleStr += `background-color: #F0F2F5;` } //将单元格设置为宽度百分比 if cellData.WidthPercent > 0 { widthDecimal := decimal.NewFromFloat(cellData.WidthPercent) cellOtherStr += ` width="` + widthDecimal.String() + `%" ` } //文字排版(居中、左、右) if cellData.TextAlign != "" { cellOtherStr += ` align="` + cellData.TextAlign + `" ` } //cell.Properties().SetAli //设置是否加粗 if cellData.IsBold { styleStr += `font-weight:bold;` } //设置字体大小 fontSize := 10.0 if cellData.FontSize > 0 { fontSize = cellData.FontSize } fontDecimal := decimal.NewFromFloat(fontSize) styleStr += `font-size: ` + fontDecimal.String() + `pt;` bodyStr := cellData.Value styleStr += `" ` cellStr += tdStr + styleStr + cellOtherStr + `>` + bodyStr + `
` return } //根据html生成pdf func Html2Pdf(htmlStr, pdfPath string) (err error) { pdfg, err := wkhtml.NewPDFGenerator() if err != nil { fmt.Println("err:", err) return } //通过html生成page page := wkhtml.NewPageReader(strings.NewReader(htmlStr)) //页眉设置 //page.HeaderLeft.Set("弘则弥道(上海)投资咨询有限公司") //page.HeaderFontName.Set("宋体") //page.HeaderFontSize.Set(8) //page.HeaderSpacing.Set(4) //page.HeaderLine.Set(true) //page.HeaderSpacing.Set(10) //页脚设置 page.FooterFontSize.Set(8) page.FooterRight.Set("[page]") page.FooterSpacing.Set(4) //page.FooterLine.Set(true) //page.EnableForms.Set(false) //转换HTML表单为PDF表单 //page.EnableForms.Set(true) //使用打印媒体类型而不是屏幕 //page.PrintMediaType.Set(true) //允许从标题链接到目录 page.EnableTocBackLinks.Set(true) //将该page插入到pdf中 pdfg.AddPage(page) //pdfg.PageSize.Set(wkhtml.PageSizeA4) //pdfg.MarginTop.Set(20) //pdfg.MarginBottom.Set(5) //pdfg.MarginLeft.Set(0) //pdfg. err = pdfg.Create() if err != nil { return } err = pdfg.WriteFile(pdfPath) return } //获取表格列数据 func getColList(item *contract_service_detail.ContractServiceDetail) (cellList []string, err error) { cellList = make([]string, 0) var serviceDetailReq contractReq.AddContractServiceDetailReq tmpItem := *item t := reflect.TypeOf(tmpItem) v := reflect.ValueOf(tmpItem) for k := 0; k < t.NumField(); k++ { //获取结构体的参数名 tmpName := t.Field(k).Name if strings.Contains(tmpName, "Col") { //获取结构体该参数名的值 tmpValue := v.Field(k).String() //如果值不为空的话,那么做下json转换 if tmpValue != "" { err = json.Unmarshal([]byte(tmpValue), &serviceDetailReq) if err != nil { return } else { cellList = append(cellList, serviceDetailReq.Value) } } } } return }