func-tbook-export.js 8.4 KB

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