import { replaceDocx, docsToPdf, createZip, uploadFileToOSS } from "../../lib/docs"; // const Parse = global.Parse; const path = require("path") const fs = require("fs") var TemplateDocxPath = path.join(__dirname,"template/模板-推荐申报表.docx") if(!fs.existsSync(TemplateDocxPath)){ TemplateDocxPath = path.join(__dirname,"../../template/模板-推荐申报表.docx") } /** * 定义导出申报合集文档云函数 * @example * Cloud Code test 导出流程 curl -X POST -H "Content-Type: application/json" -H 'X-Parse-Application-Id: edu-textbook' -d '{ "processId": "Wz34loxdbO" }' http://127.0.0.1:61337/parse/functions/tbookExportReport curl -X POST -H "Content-Type: application/json" -H 'X-Parse-Application-Id: edu-textbook' -d '{ "processId": "Wz34loxdbO" }' http://8.140.98.43/parse/functions/tbookExportReport 导出教材列表 curl -X POST -H "Content-Type: application/json" -H 'X-Parse-Application-Id: edu-textbook' -d '{ "bookList": ["2YBKitpCJL","xLdiEaHGrX"] }' http://127.0.0.1:61337/parse/functions/tbookExportReport */ export function defineTbookExportReport(){ Parse.Cloud.define("tbookExportReport", async (request) => { let processId = request.params.processId; let bookList = request.params.bookList; try{ let result if(processId){ await exportProcessReportDocs(processId) } if(bookList?.length){ await exportProcessReportDocs(null,bookList) } if(result?.docsList?.length==0){ throw new Parse.Error(404,"合集内无申报教材") } return result }catch(err){ console.error(err) throw new Parse.Error(404,"导出申报合集失败") } throw new Parse.Error(404,"未找到该流程合集") },{ fields : { processId:{ required:false }, } }); } /** * 导出流程教材申报文件 * @returns * docsList * zipUrl */ export async function exportProcessReportDocs(processId,bookList) { if(!processId && !bookList?.length) return {} let textbookList if(processId){ // 流程读取教材列表 let query = new Parse.Query("EduTextbook") query.equalTo("eduProcess",processId); textbookList = await query.find(); } if(bookList?.length){ // 直接导出教材列表 let query = new Parse.Query("EduTextbook") query.containedIn("objectId",bookList); textbookList = await query.find(); } let docsList = [] for (let index = 0; index < textbookList.length; index++) { let textbook = textbookList[index]; let result = await renderReportDocsByTextbook(textbook); docsList.push(result) } let zipPath,zipUrl if(docsList?.length){ let now = new Date(); let zipName = `申报书导出-${now.getFullYear()}${now.getMonth()+1}${now.getDate()}-${now.getHours()}${now.getMinutes()}${now.getSeconds()}.zip` zipPath = await createZip(docsList?.map(item=>item?.filePath),zipName) if(zipPath){ zipUrl = (await uploadFileToOSS(zipPath))?.url || null } docsList = docsList.map(item=>{return {code:item.code,title:item.title,url:item?.url}}) } let result = { docsList, zipUrl } return result } module.exports.exportProcessReportDocs = exportProcessReportDocs function renderReportDocsByTextbook(textbook){ let json = textbook.toJSON(); console.log(json) // 圆圈选中未选 ○ 未选 ● 选中 let circleCheck = ["○","●"]; // 方块选中未选 ○ 未选 ● 选中 let squareCheck = [``,`☑`]; // 联系电话:默认为作者首个存在的电话; let mobile = json?.authorList?.find(item => item.mobile)?.mobile || "" // 填报时间:默认为创建时间 let createdAt = new Date(textbook?.createdAt); let createdDate = `${createdAt?.getFullYear()}年${createdAt?.getMonth()+1}月${createdAt?.getDate()}日`; // 专业代码:前四位 let majorCode = json?.majorId || json?.major?.code if(majorCode?.length>4){ majorCode = majorCode.slice(0,4) } let majorName = json?.majorName || json?.major?.name // 是否重点立项 let isJC = circleCheck[(json?.approval?.indexOf("基础")>-1)?1:0]; let isZL = circleCheck[(json?.approval?.indexOf("战略")>-1)?1:0]; let is101 = circleCheck[(json?.approval?.indexOf("101计划")>-1)?1:0]; // 2024新重点 let isZY = circleCheck[(json?.approval?.indexOf("中央")>-1)?1:0]; let isSX = circleCheck[(json?.approval?.indexOf("四新")>-1)?1:0]; let isJS = circleCheck[(json?.importantProject?.indexOf("建设")>-1)?1:0]; let isNotImpt = (json?.approval?.indexOf("101计划")==-1) && (json?.approval?.indexOf("中央")==-1) && (json?.approval?.indexOf("四新")==-1) && (json?.approval?.indexOf("建设")==-1) isNotImpt = circleCheck[isNotImpt?1:0]; // 初版时间 let firstDate = new Date(textbook?.get("editionFirst")); let firstYear = firstDate?.getFullYear(); let firstMonth = firstDate?.getMonth()+1; // 本版时间印次 let currentDate = new Date(textbook?.get("editionDate")); let currentYear = currentDate?.getFullYear(); let currentMonth = currentDate?.getMonth()+1; // 最新时间印次 let latestDate = new Date(textbook?.get("printDate")); let latestYear = latestDate?.getFullYear(); let latestMonth = latestDate?.getMonth()+1; // 初版至今重点项目 let isBSQT = !((json?.importantProject?.indexOf("建设")>-1) || (json?.importantProject?.indexOf("本科国家")>-1) || (json?.importantProject?.indexOf("省级优秀")>-1) || (json?.importantProject?.indexOf("省级规划")>-1))// 是否其他省级奖项 let bookData = { // 封面信息 titlePad:padString(json?.title,21), ISBN:padString(json?.ISBN,21), one:squareCheck[(json?.type=="单本"||json?.type=="单册")?1:0], // 单本/单册 方框 full:squareCheck[json?.type=="全册"?1:0], // 全册 oneCircle:circleCheck[(json?.type=="单本"||json?.type=="单册")?1:0], // 单本/单册 圆圈 fullCircle:circleCheck[json?.type=="全册"?1:0], // 全册 tn:json?.typeNumber, authorPad:padString(json?.author,21), mobile:padString(mobile,21), authorUnit:padString(json?.unit,21), publisherPad:padString(json?.editionUnit,21), recommandUnit:padString("",14), // 未找到 majorCodePad:padString((majorCode),14), createdDate:padString(createdDate,21), // 基本信息 title:json?.title, author:json?.author, unit:json?.unit, mc:majorCode, mn:majorName, lCN:circleCheck[(json?.lang=="中文")?1:0], lEN:circleCheck[(json?.lang=="英文")?1:0], lOT:circleCheck[(json?.lang?.indexOf("其他")>-1)?1:0], lSS:circleCheck[(json?.lang?.indexOf("少数")>-1)?1:0], authors:json?.authors, // 其他主编 editor:json?.editor, // 其他编者 isJC:isJC, isZL:isZL, isSX:isSX, is101:is101, isZY:isZY, isJS:isJS, isNotImpt:isNotImpt, publisher:json?.editionUnit, firstYear:firstYear, firstMonth:firstMonth, isZZ:circleCheck[(json?.carrierShape?.indexOf("纸质")>-1)?1:0], isDZ:circleCheck[(json?.carrierShape?.indexOf("电子")>-1)?1:0], isSZ:circleCheck[(json?.carrierShape?.indexOf("数字")>-1)?1:0], isQT:circleCheck[(json?.carrierShape?.indexOf("附带")>-1)?1:0], isFD:circleCheck[(json?.carrierShape?.indexOf("其他")>-1)?1:0], latestY:latestYear, latestM:latestMonth, latestNum:json?.printNumber || "", currentY:currentYear, currentM:currentMonth, currentNum:json?.editionNumber || "", printSum:json?.printSum || "", isBGJ:circleCheck[(json?.importantProject?.indexOf("本科国家")>-1)?1:0], isBSYX:circleCheck[(json?.importantProject?.indexOf("省级优秀")>-1)?1:0], isBSGH:circleCheck[(json?.importantProject?.indexOf("省级规划")>-1)?1:0], isBSQT:circleCheck[isBSQT?1:0], bsqtName:isBSQT?(json?.importantProject || ""):"", isFirstNot:circleCheck[json?.importantProject?0:1], } console.log(bookData) let bookid = json.code || json?.objectId; let tempFileName = path.join(`${bookid}${json.title}.docx`) return new Promise((resolve)=>{ replaceDocx(TemplateDocxPath,tempFileName,bookData,{onDocxComplete:async (filePath)=>{ // 需要API支持 // docsToPdf(filePath) let url = (await uploadFileToOSS(filePath))?.url || null resolve({ code:bookid, title:json?.title, filePath, url }) }}) }) } function padString(str,width) { str = str || "" str = String(str) width = width || 21 // 目标宽度为21个单位 let spaceChar = " " // 占位符 // 计算字符串的宽度 const charWidth = { 'space': 1, // 空格占用1个单位 'zh': 2, // 汉字占用2个单位 'en': 1, // 英文字母占用1个单位 'other': 1 // 其他字符(如标点符号)占用1个单位 }; let strWidth = 0; console.log(str) // 遍历文本中的每个字符 for (let char of str) { if (/\s/.test(char)) { strWidth += charWidth.space; // 空格 } else if (/[\u4e00-\u9fa5]/.test(char)) { strWidth += charWidth.zh; // 汉字 } else if (/[a-zA-Z]/.test(char)) { strWidth += charWidth.en; // 英文字母 } else { strWidth += charWidth.other; // 其他字符 } } // for (let char of str) { // // 判断字符是否为中文 // if (char.match(/[\u4e00-\u9fa5]/)) { // strWidth += 2; // 中文字符占4个单位 // } else { // strWidth += 1; // 英文字符占1个单位 // } // } const totalPadding = width - strWidth; // 如果已经达到或超过目标宽度,直接返回原字符串 if (totalPadding <= 0) { return str; } // 计算左右两侧的空格数 const leftPadding = Math.floor(totalPadding / 2) * 3; const rightPadding = Math.ceil(totalPadding / 2) * 3; // 生成填充空格的字符串 const leftSpaces = spaceChar.repeat(leftPadding); const rightSpaces = spaceChar.repeat(rightPadding); // 返回补充后的字符串 return leftSpaces + str + rightSpaces; }