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" contractCustom "hongze/hongze_mobile_admin/models/custom/contract" 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" "os" "os/exec" "path" "reflect" "strconv" "strings" "time" ) 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 := getContractAddress(contractDetail) 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 := utils.FormatPrice(contractDetail.OriginalPrice) 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 := utils.FormatPrice(contractDetail.Price) 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客户服务列表2022》中 小套餐 的服务内容,详细如下:" } 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:"甲方地址"` PostcodeDisplay string `description:"甲方邮编;是否展示"` Postcode string `description:"甲方邮编"` PhoneDisplay string `description:"甲方电话;是否展示"` Phone string `description:"甲方电话"` FaxDisplay string `description:"传真;是否展示"` Fax string `description:"传真"` RemarkDisplay 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 := getContractAddress(contractDetail) data := html2pdfData{ CompanyName: contractDetail.CompanyName, ContractCode: contractDetail.ContractCode, Address: address, PostcodeDisplay: "block", Postcode: contractDetail.Postcode, PhoneDisplay: "block", Phone: contractDetail.Phone, FaxDisplay: "block", Fax: contractDetail.Fax, RemarkDisplay: "block", 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 = "无" data.PostcodeDisplay = "none" } if data.Fax == "" { data.Fax = "无" data.FaxDisplay = "none" } if data.Phone == "" { data.Phone = "无" data.PhoneDisplay = "none" } if data.PayRemark == "" { data.PayRemark = "无" } if data.Remark == "" { data.Remark = "无" data.RemarkDisplay = "none" } //合同有效期 { ////合同结束日期与合同开始日期的时间差(小时差) //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() tmpPrintContent, tmpErr := utils.CalculationDate(contractDetail.StartDate, contractDetail.EndDate) if tmpErr != nil { err = tmpErr return } data.NumYear = tmpPrintContent } //合同金额 { priceStr := "" //originalPrice := strconv.FormatFloat(contractDetail.OriginalPrice, 'E', -1, 64) //优惠前金额(小写) //newDecimal := decimal.NewFromFloat(contractDetail.OriginalPrice) originalPrice := utils.FormatPrice(contractDetail.OriginalPrice) 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 := utils.FormatPrice(contractDetail.Price) 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() // 初始化合同中一些特定区域的文字展示配置 contentConfigMap := make(map[string]string) err = json.Unmarshal([]byte(contractTemplate.ContentConfig), &contentConfigMap) if err != nil { return } //服务内容 { tableStr := "" tableDataSlice := make([]TableData, 0) tableTitleSlice := make([]string, 0) title := `` if tmpTitle, ok := contentConfigMap["title1"]; ok { title = tmpTitle } 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 { //赋值table表格数据 jsonStr := item.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 } type WordElement struct { ElementType string `json:"element_type" description:"元素类型"` ElementName string `json:"element_name" description:"元素名称"` RelationName string `json:"relation_name" description:"关联元素名称"` Content string `json:"content" description:"元素内容"` Background string `json:"background" description:"背景色"` IsBold bool `json:"is_bold" description:"是否加粗显示"` TextAlign string `json:"text_align" description:"对齐方式"` FontSize float64 `json:"font_size" description:"字体大小"` ElementList []WordElement `json:"list" description:"子元素"` } // GenerateWordV2 生成word func GenerateWordV2(contractDetail *contract.ContractDetail, wordPath string) (err error) { //临时合同信息 tmpContractDetail := *contractDetail //合同服务 contractServiceAndDetailList := make([]*contractCustom.ContractServiceAndDetail, 0) //服务内容 if tmpContractDetail.ContractBusinessType == "代付合同" { if contractDetail.RelationContractDetailList != nil && len(contractDetail.RelationContractDetailList) > 0 { tmpContractDetail.StartDate = tmpContractDetail.RelationContractDetailList[0].StartDate tmpContractDetail.EndDate = tmpContractDetail.RelationContractDetailList[0].EndDate contractServiceAndDetailList = tmpContractDetail.RelationContractDetailList[0].Service } } else { contractServiceAndDetailList = tmpContractDetail.Service } //模板信息 contractTemplate, err := contract_template.GetContractTemplateByTemplateId(contractDetail.TemplateId) if err != nil { return } jsonStr := contractTemplate.WordConfig var contractData []WordElement err = json.Unmarshal([]byte(jsonStr), &contractData) if err != nil { fmt.Println("json字符串解析失败,ERR:", err) return } // 初始化合同中一些特定区域的文字展示配置 contentConfigMap := make(map[string]string) err = json.Unmarshal([]byte(contractTemplate.ContentConfig), &contentConfigMap) if err != nil { return } doc := document.New() //word的属性设置,类型,作者之类的 cp := doc.CoreProperties // And change them as well cp.SetTitle("弘则弥道(上海)投资咨询有限公司 & 研究服务合同") cp.SetAuthor("弘则弥道(上海)投资咨询有限公司") cp.SetCategory("合同") //cp.SetContentStatus("Draft") cp.SetLastModifiedBy("弘则弥道(上海)投资咨询有限公司") cp.SetCreated(time.Now()) cp.SetModified(time.Now()) cp.SetDescription("弘则弥道(上海)投资咨询有限公司 研究服务合同") cp.SetLanguage("中文") tableFontSize := 10.5 //默认表格内文字10.5 for _, data := range contractData { fontSize := data.FontSize if fontSize <= 0 { fontSize = 15 } printContent := `` if data.ElementName == "services" { tableTitleSlice := make([]string, 0) title := `` if tmpTitle, ok := contentConfigMap["title1"]; ok { title = tmpTitle } TableDataListSlice := make([]TableData, 0) //for i := len(contractServiceAndDetailList) - 1; i >= 0; i-- { for i := 0; i < len(contractServiceAndDetailList); i++ { //表格数据 var tableDataList TableData item := contractServiceAndDetailList[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 := tableFontSize //表头数据样式 if j == 0 { isBold = true backgrandColor = "gray_2" } //获取每一列的数据 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 } else { //赋值table表格数据 jsonStr := item.TableValue err = json.Unmarshal([]byte(jsonStr), &tableDataList) if err != nil { return } } //往word中添加表格数据 TableDataListSlice = append(TableDataListSlice, tableDataList) } //表格标题 titleStr := strings.Join(tableTitleSlice, "、") title += titleStr + "的服务内容,详细如下:" //开始一个新的段落 headerPar := doc.AddParagraph() headerParPro := headerPar.Properties() //headerParPro.AddTabStop() textAlign := getTextAlignConf(data.TextAlign) headerParPro.SetAlignment(textAlign) //if data.ElementType == "column" { // headerParPro.SetAlignment(wml.ST_JcBoth) //} headerRun := headerPar.AddRun() headerRunPro := headerRun.Properties() headerRunPro.SetBold(data.IsBold) headerRunPro.SetSize(measurement.Distance(fontSize * measurement.Point)) headerRunPro.SetFontFamily("宋体") //headerRunPro.SetKerning(measurement.Distance(2 * fontSize * measurement.Point)) headerRun.AddText(title) //生成表格 for _, tableDataList := range TableDataListSlice { tmpErr := addTableV2(tableDataList, doc) if tmpErr != nil { err = tmpErr return } } continue } else { isPrint, tmpPrintContent, tmpErr := getPrintData(data, contractDetail) if tmpErr != nil { err = tmpErr return } if isPrint == false { continue } printContent = tmpPrintContent } //分栏的宽度 printContentRune := []rune(printContent) strLen := len(printContentRune) addTabNum := 0 printContentList := make([]map[int]string, 0) firstLen := 17 secondLen := 14 if data.ElementType == "column" { if strLen > firstLen { maxLine := ((strLen - firstLen) / secondLen) + 1 for i := 0; i < maxLine; i++ { printContentMap := make(map[int]string) startIndex := secondLen*i + firstLen endIndex := secondLen*(i+1) + firstLen if endIndex > strLen { endIndex = strLen } tmpPrintContent := string(printContentRune[startIndex:endIndex]) printContentMap[0] = tmpPrintContent printContentList = append(printContentList, printContentMap) } printContent = string(printContentRune[:firstLen]) strLen = firstLen //重新计算宽度 //width = fontSize * float64(strLen) //addTabNum = 3 } else { addTabNum = firstLen - strLen } addTabNum += 3 } //开始一个新的段落 headerPar := doc.AddParagraph() headerParPro := headerPar.Properties() headerParPro.Spacing().SetLineSpacing(measurement.Distance(1.5*fontSize*measurement.Point), wml.ST_LineSpacingRuleAuto) //headerParPro.AddTabStop() textAlign := getTextAlignConf(data.TextAlign) headerParPro.SetAlignment(textAlign) //if data.ElementType == "column" { // headerParPro.SetAlignment(wml.ST_JcBoth) //} headerRun := headerPar.AddRun() headerRunPro := headerRun.Properties() headerRunPro.SetBold(data.IsBold) headerRunPro.SetSize(measurement.Distance(fontSize * measurement.Point)) headerRunPro.SetFontFamily("宋体") //headerRunPro.SetKerning(measurement.Distance(2 * fontSize * measurement.Point)) headerRun.AddText(printContent) for _, text := range data.ElementList { if text.ElementName == "services" { tableTitleSlice := make([]string, 0) title := `` if tmpTitle, ok := contentConfigMap["title1"]; ok { title = tmpTitle } TableDataListSlice := make([]TableData, 0) for i := len(contractServiceAndDetailList) - 1; i >= 0; i-- { //表格数据 var tableDataList TableData item := contractServiceAndDetailList[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 := tableFontSize //表头数据样式 if j == 0 { isBold = true backgrandColor = "gray_2" } //获取每一列的数据 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 } else { //赋值table表格数据 jsonStr := item.TableValue err = json.Unmarshal([]byte(jsonStr), &tableDataList) if err != nil { return } } //往word中添加表格数据 TableDataListSlice = append(TableDataListSlice, tableDataList) } //表格标题 titleStr := strings.Join(tableTitleSlice, "、") title += titleStr + "的服务内容,详细如下:" headerRun := headerPar.AddRun() headerRun.AddBreak() headerRunPro := headerRun.Properties() headerRunPro.SetBold(text.IsBold) headerRunPro.SetSize(measurement.Distance(fontSize * measurement.Point)) headerRunPro.SetFontFamily("宋体") //headerRunPro.SetKerning(measurement.Distance(2 * fontSize * measurement.Point)) headerRun.AddText(title) //生成表格 for _, tableDataList := range TableDataListSlice { tmpErr := addTableV2(tableDataList, doc) if tmpErr != nil { err = tmpErr return } } } else { isPrint, printContent, tmpErr := getPrintData(text, contractDetail) if tmpErr != nil { err = tmpErr return } if isPrint == false { continue } if data.ElementType == "column" { for j := 0; j < addTabNum; j++ { //headerRun.AddTab() tabStr := " " printContent = tabStr + printContent } } fontSize := text.FontSize if fontSize <= 0 { fontSize = 15 } headerRun2 := headerPar.AddRun() headerRunPro2 := headerRun2.Properties() headerRunPro2.SetBold(text.IsBold) headerRunPro2.SetSize(measurement.Distance(fontSize * measurement.Point)) headerRunPro2.SetFontFamily("宋体") //headerRunPro2.SetKerning(measurement.Distance(2 * fontSize * measurement.Point)) headerRun2.AddText(printContent) } } for _, printMap := range printContentList { //开始一个新的段落 headerPar := doc.AddParagraph() headerParPro := headerPar.Properties() headerParPro.Spacing().SetLineSpacing(measurement.Distance(1.5*fontSize*measurement.Point), wml.ST_LineSpacingRuleAuto) //headerParPro.AddTabStop() textAlign := getTextAlignConf(data.TextAlign) headerParPro.SetAlignment(textAlign) //空出三个字符出来,避免签名顶在最前方展示 headerParPro.SetStartIndent(measurement.Distance(3 * fontSize * measurement.Point)) headerRun := headerPar.AddRun() headerRunPro := headerRun.Properties() headerRunPro.SetBold(data.IsBold) headerRunPro.SetSize(measurement.Distance(fontSize * measurement.Point)) headerRunPro.SetFontFamily("宋体") //headerRunPro.SetKerning(measurement.Distance(2 * fontSize * measurement.Point)) headerRun.AddText(printMap[0]) } } //for _, data := range list { // headerPar := doc.AddParagraph() // headerParPro := headerPar.Properties() // headerParPro.SetAlignment(wml.ST_JcCenter) // headerRun := headerPar.AddRun() // headerRunPro := headerRun.Properties() // headerRunPro.SetBold(true) // headerRunPro.SetSize(16) // headerRun.AddText(data) //} err = doc.SaveToFile(wordPath) return } // getPrintData 获取打印数据 func getPrintData(data WordElement, contractDetail *contract.ContractDetail) (isPrint bool, printContent string, err error) { printContent = data.Content if data.RelationName != "" { switch data.RelationName { case "address": if contractDetail.Address == "" && contractDetail.Province == "" && contractDetail.City == "" { return } case "postcode": if contractDetail.Postcode == "" { return } case "phone": if contractDetail.Phone == "" { return } case "fax": if contractDetail.Fax == "" { return } case "remark": if contractDetail.Remark == "" { return } case "price": //实际金额(小写) if contractDetail.OriginalPrice == contractDetail.Price { return } case "price_cn": //实际金额(大写) if contractDetail.OriginalPrice == contractDetail.Price { return } case "pay_remark": if contractDetail.PayRemark == "" { return } case "company_name": if contractDetail.CompanyName == "" { return } } } switch data.ElementName { case "address": if contractDetail.Address == "" && contractDetail.Province == "" && contractDetail.City == "" { return } printContent = getContractAddress(contractDetail) case "postcode": if contractDetail.Postcode == "" { return } printContent = contractDetail.Postcode case "phone": if contractDetail.Phone == "" { return } printContent = contractDetail.Phone case "fax": if contractDetail.Fax == "" { return } printContent = contractDetail.Fax case "remark": if contractDetail.Remark == "" { return } printContent = contractDetail.Remark case "start_date": printContent = contractDetail.StartDate.Format("2006年01月02日") case "end_date": printContent = contractDetail.EndDate.Format("2006年01月02日") case "num_year": ////合同结束日期与合同开始日期的时间差(小时差) //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 //} //printContent = numYearDecimal.String() tmpPrintContent, tmpErr := utils.CalculationDate(contractDetail.StartDate, contractDetail.EndDate) if tmpErr != nil { err = tmpErr return } printContent = tmpPrintContent case "original_price": //优惠前金额(小写) //newDecimal := decimal.NewFromFloat(contractDetail.OriginalPrice) printContent = utils.FormatPrice(contractDetail.OriginalPrice) case "original_price_cn": //优惠前金额(大写) originalCnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.OriginalPrice) if cnyErr != nil { err = cnyErr return } printContent = originalCnyPrice case "price": //实际金额(小写) if contractDetail.OriginalPrice == contractDetail.Price { return } //newDecimal := decimal.NewFromFloat(contractDetail.Price) printContent = utils.FormatPrice(contractDetail.Price) case "price_cn": //实际金额(大写) if contractDetail.OriginalPrice == contractDetail.Price { return } cnyPrice, cnyErr := utils.ConvertNumToCny(contractDetail.Price) if cnyErr != nil { err = cnyErr return } printContent = cnyPrice case "pay_remark": if contractDetail.PayRemark == "" { return } printContent = contractDetail.PayRemark case "company_name": if contractDetail.CompanyName == "" { return } printContent = contractDetail.CompanyName case "company_name_sign": if contractDetail.CompanyName == "" { return } printContent = "甲方:" + contractDetail.CompanyName } isPrint = true return } // addTableV2 添加表格数据 func addTableV2(tableDataList TableData, doc *document.Document) (err error) { //fmt.Println("表头名称:", title) //插入一个新的段落 //nowParagraph := doc.AddParagraph() ////表格数据 //table := doc.InsertTableAfter(nowParagraph) //表格数据 doc.AddParagraph().Properties().AddSection(wml.ST_SectionMarkNextPage) //分页符 table := doc.AddTable() //设置表格宽度 tableWidth := 6.5 table.Properties().SetWidth(measurement.Distance(tableWidth * measurement.Inch)) //表格宽度设置为自动 //table.Properties().SetWidthAuto() //边框 borders := table.Properties().Borders() // thin borders borders.SetAll(wml.ST_BorderSingle, color.Auto, measurement.Zero) //表格数据 rowList := tableDataList.List //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++ { 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 { //合并单元格类型 var mergeVal wml.ST_Merge if cellData.IsFirstMerged { //如果是第一个合并行,那么开始重新合并 mergeVal = wml.ST_MergeRestart } else { //如果不是第一个合并行,那么是继续合并 mergeVal = wml.ST_MergeContinue } cell.Properties().SetVerticalMerge(mergeVal) } //背景色 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) //因为libreOffice不支持百分比的设置表格宽度 cellWidth := tableWidth * cellData.WidthPercent * measurement.Inch / 100 cell.Properties().SetWidth(measurement.Distance(cellWidth)) } //文字排版(居中、左、右) 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("") } } } } doc.AddParagraph() return } // getContractAddress 获取展示的详细地址 func getContractAddress(contractDetail *contract.ContractDetail) (address string) { ignoreStrs := []string{"北京市", "上海市", "天津市", "重庆市"} if strings.Contains(strings.Join(ignoreStrs, ","), contractDetail.Province) { address = contractDetail.City + contractDetail.Address } else { address = contractDetail.Province + contractDetail.City + contractDetail.Address } return } /** *@tips libreoffice 转换指令: * libreoffice6.2 invisible --convert-to pdf csDoc.doc --outdir /home/[转出目录] * * @function 实现文档类型转换为pdf或html * @param command:libreofficed的命令(具体以版本为准);win:soffice; linux:libreoffice6.2 * fileSrcPath:转换文件的路径 * fileOutDir:转换后文件存储目录 * converterType:转换的类型pdf/html * @return fileOutPath 转换成功生成的文件的路径 error 转换错误 */ func FuncDocs2Pdf(command string, fileSrcPath string, fileOutDir string, converterType string) (fileOutPath string, error error) { //校验fileSrcPath srcFile, erByOpenSrcFile := os.Open(fileSrcPath) if erByOpenSrcFile != nil && os.IsNotExist(erByOpenSrcFile) { return "", erByOpenSrcFile } //如文件输出目录fileOutDir不存在则自动创建 outFileDir, erByOpenFileOutDir := os.Open(fileOutDir) if erByOpenFileOutDir != nil && os.IsNotExist(erByOpenFileOutDir) { erByCreateFileOutDir := os.MkdirAll(fileOutDir, 0766) if erByCreateFileOutDir != nil { fmt.Println("File ouput dir create error.....", erByCreateFileOutDir.Error()) return "", erByCreateFileOutDir } } //关闭流 defer func() { _ = srcFile.Close() _ = outFileDir.Close() }() //convert cmd := exec.Command(command, "--invisible", "--convert-to", converterType, fileSrcPath, "--outdir", fileOutDir) byteByStat, errByCmdStart := cmd.Output() //命令调用转换失败 if errByCmdStart != nil { return "", errByCmdStart } //success fileOutPath = fileOutDir + "/" + strings.Split(path.Base(fileSrcPath), ".")[0] if converterType == "html" { fileOutPath += ".html" } else { fileOutPath += ".pdf" } fmt.Println("文件转换成功...", string(byteByStat)) return fileOutPath, nil }