func-tbook-export.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import { replaceDocx, docsToPdf, createZip, uploadFileToOSS } from "../../lib/docs";
  2. // const Parse = global.Parse;
  3. const path = require("path")
  4. const TemplateDocxPath = path.join(__dirname,"template/模板-推荐申报表.docx")
  5. /**
  6. * 定义导出申报合集文档云函数
  7. * @example
  8. * Cloud Code test
  9. 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
  10. 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
  11. */
  12. export function defineTbookExportReport(){
  13. Parse.Cloud.define("tbookExportReport", async (request) => {
  14. let processId = request.params.processId;
  15. if(processId) {
  16. try{
  17. let result = await exportProcessReportDocs(processId)
  18. if(result?.docsList?.length==0){
  19. throw new Parse.Error(404,"合集内无申报教材")
  20. }
  21. return result
  22. }catch(err){
  23. console.error(err)
  24. throw new Parse.Error(404,"导出申报合集失败")
  25. }
  26. }
  27. throw new Parse.Error(404,"未找到该流程合集")
  28. },{
  29. fields : {
  30. processId:{
  31. required:true
  32. }
  33. }
  34. });
  35. }
  36. /**
  37. * 导出流程教材申报文件
  38. * @returns
  39. * docsList
  40. * zipUrl
  41. */
  42. async function exportProcessReportDocs(processId) {
  43. if(!processId) return {}
  44. let query = new Parse.Query("EduTextbook")
  45. query.equalTo("eduProcess",processId);
  46. let textbookList = await query.find();
  47. let docsList = []
  48. for (let index = 0; index < textbookList.length; index++) {
  49. let textbook = textbookList[index];
  50. let result = await renderReportDocsByTextbook(textbook);
  51. docsList.push(result)
  52. }
  53. let zipPath,zipUrl
  54. if(docsList?.length){
  55. zipPath = await createZip(docsList?.map(item=>item?.filePath),`流程合集-${processId}.zip`)
  56. if(zipPath){
  57. zipUrl = (await uploadFileToOSS(zipPath))?.url || null
  58. }
  59. docsList = docsList.map(item=>{return {code:item.code,title:item.title,url:item?.url}})
  60. }
  61. console.log(textbookList);
  62. console.log(docsList)
  63. console.log(processId)
  64. let result = {
  65. docsList,
  66. zipUrl
  67. }
  68. return result
  69. }
  70. module.exports.exportProcessReportDocs = exportProcessReportDocs
  71. function renderReportDocsByTextbook(textbook){
  72. let json = textbook.toJSON();
  73. // 圆圈选中未选 ○ 未选 ● 选中
  74. let circleCheck = ["○","●"];
  75. // 方块选中未选 ○ 未选 ● 选中
  76. let squareCheck = [``,`☑`];
  77. // 联系电话:默认为作者首个存在的电话;
  78. let mobile = json?.authorList?.find(item => item.mobile)?.mobile || ""
  79. // 填报时间:默认为创建时间
  80. let createdAt = new Date(textbook?.createdAt);
  81. let createdDate = `${createdAt?.getFullYear()}年${createdAt?.getMonth()+1}月${createdAt?.getDate()}日`;
  82. // 专业代码:前四位
  83. let majorCode = json?.majorId || json?.major?.code
  84. if(majorCode?.length>4){
  85. majorCode = majorCode.slice(0,4)
  86. }
  87. let majorName = json?.majorName || json?.major?.name
  88. // 是否重点立项
  89. let isJC = circleCheck[(json?.approval?.indexOf("基础")>-1)?1:0];
  90. let isZL = circleCheck[(json?.approval?.indexOf("战略")>-1)?1:0];
  91. let isSX = circleCheck[(json?.approval?.indexOf("四新")>-1)?1:0];
  92. let isNotImpt = (json?.approval?.indexOf("基础")==-1) && (json?.approval?.indexOf("战略")==-1) && (json?.approval?.indexOf("四新")==-1)
  93. isNotImpt = circleCheck[isNotImpt?1:0];
  94. // 初版时间
  95. let firstDate = new Date(textbook?.get("editionFirst"));
  96. let firstYear = firstDate?.getFullYear();
  97. let firstMonth = firstDate?.getMonth()+1;
  98. // 本版时间印次
  99. let currentDate = new Date(textbook?.get("editionDate"));
  100. let currentYear = currentDate?.getFullYear();
  101. let currentMonth = currentDate?.getMonth()+1;
  102. // 最新时间印次
  103. let latestDate = new Date(textbook?.get("printDate"));
  104. let latestYear = latestDate?.getFullYear();
  105. let latestMonth = latestDate?.getMonth()+1;
  106. // 初版至今重点项目
  107. let isBSQT = !((json?.importantProject?.indexOf("建设")>-1) || (json?.importantProject?.indexOf("本科国家")>-1) || (json?.importantProject?.indexOf("省级优秀")>-1) || (json?.importantProject?.indexOf("省级规划")>-1))// 是否其他省级奖项
  108. let bookData = {
  109. // 封面信息
  110. titlePad:padString(json?.title,21),
  111. ISBN:padString(json?.ISBN,21),
  112. one:squareCheck[(json?.type=="单本"||json?.type=="单册")?1:0], // 单本/单册 方框
  113. full:squareCheck[json?.type=="全册"?1:0], // 全册
  114. oneCircle:circleCheck[(json?.type=="单本"||json?.type=="单册")?1:0], // 单本/单册 圆圈
  115. fullCircle:circleCheck[json?.type=="全册"?1:0], // 全册
  116. tn:json?.typeNumber,
  117. authorPad:padString(json?.author,21),
  118. mobile:padString(mobile,21),
  119. authorUnit:padString(json?.unit,21),
  120. publisherPad:padString(json?.editionUnit,21),
  121. recommandUnit:padString("",21), // 未找到
  122. majorCodePad:padString((majorCode),14),
  123. createdDate:padString(createdDate,21),
  124. // 基本信息
  125. title:json?.title,
  126. author:json?.author,
  127. unit:json?.unit,
  128. mc:majorCode,
  129. mn:majorName,
  130. lCN:circleCheck[(json?.lang=="中文")?1:0],
  131. lEN:circleCheck[(json?.lang=="英文")?1:0],
  132. lOT:circleCheck[(json?.lang?.indexOf("其他")>-1)?1:0],
  133. lSS:circleCheck[(json?.lang?.indexOf("少数")>-1)?1:0],
  134. authors:json?.authors, // 其他主编
  135. editor:json?.editor, // 其他编者
  136. isJC:isJC,
  137. isZL:isZL,
  138. isSX:isSX,
  139. isNotImpt:isNotImpt,
  140. publisher:json?.editionUnit,
  141. firstYear:firstYear,
  142. firstMonth:firstMonth,
  143. isZZ:circleCheck[(json?.carrierShape?.indexOf("纸质")>-1)?1:0],
  144. isDZ:circleCheck[(json?.carrierShape?.indexOf("电子")>-1)?1:0],
  145. isSZ:circleCheck[(json?.carrierShape?.indexOf("数字")>-1)?1:0],
  146. isQT:circleCheck[(json?.carrierShape?.indexOf("附带")>-1)?1:0],
  147. isFD:circleCheck[(json?.carrierShape?.indexOf("其他")>-1)?1:0],
  148. latestY:latestYear,
  149. latestM:latestMonth,
  150. latestNum:json?.printNumber || "",
  151. currentY:currentYear,
  152. currentM:currentMonth,
  153. currentNum:json?.editionNumber || "",
  154. printSum:json?.printSum || "",
  155. isJS:circleCheck[(json?.importantProject?.indexOf("建设")>-1)?1:0],
  156. isBGJ:circleCheck[(json?.importantProject?.indexOf("本科国家")>-1)?1:0],
  157. isBSYX:circleCheck[(json?.importantProject?.indexOf("省级优秀")>-1)?1:0],
  158. isBSGH:circleCheck[(json?.importantProject?.indexOf("省级规划")>-1)?1:0],
  159. isBSQT:circleCheck[isBSQT?1:0],
  160. bsqtName:isBSQT?json?.importantProject:"",
  161. isFirstNot:circleCheck[json?.importantProject?0:1],
  162. }
  163. console.log(bookData)
  164. let bookid = json.code || json?.objectId;
  165. let tempFileName = path.join(`${bookid}${json.title}.docx`)
  166. return new Promise((resolve)=>{
  167. replaceDocx(TemplateDocxPath,tempFileName,bookData,{onDocxComplete:async (filePath)=>{
  168. // 需要API支持
  169. // docsToPdf(filePath)
  170. let url = (await uploadFileToOSS(filePath))?.url || null
  171. resolve({
  172. code:bookid,
  173. title:json?.title,
  174. filePath,
  175. url
  176. })
  177. }})
  178. })
  179. }
  180. function padString(str,width) {
  181. str = str || ""
  182. str = String(str)
  183. width = width || 21 // 目标宽度为21个单位
  184. spaceChar = "&#160;" // 占位符
  185. // 计算字符串的宽度
  186. let strWidth = 0;
  187. console.log(str)
  188. for (let char of str) {
  189. // 判断字符是否为中文
  190. if (char.match(/[\u4e00-\u9fa5]/)) {
  191. strWidth += 2; // 中文字符占4个单位
  192. } else {
  193. strWidth += 1; // 英文字符占1个单位
  194. }
  195. }
  196. const totalPadding = width - strWidth;
  197. // 如果已经达到或超过目标宽度,直接返回原字符串
  198. if (totalPadding <= 0) {
  199. return str;
  200. }
  201. // 计算左右两侧的空格数
  202. const leftPadding = Math.floor(totalPadding / 2) * 3;
  203. const rightPadding = Math.ceil(totalPadding / 2) * 3;
  204. // 生成填充空格的字符串
  205. const leftSpaces = spaceChar.repeat(leftPadding);
  206. const rightSpaces = spaceChar.repeat(rightPadding);
  207. // 返回补充后的字符串
  208. return leftSpaces + str + rightSpaces;
  209. }