app.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. //IE下找不到pdf.worker.js,需要手动赋值
  2. PDFJS.workerSrc = 'static/lib/pdf.js/build/pdf.worker.js';
  3. var canvas = document.getElementById('theCanvas');
  4. var ctx = canvas.getContext('2d');
  5. // 隐藏的iframe,用于加载pdf,以便打印(pdf.js自带的打印有问题)
  6. var hiddenFrame = document.getElementById("hiddenFrame");
  7. // 用于显示正在加载的提示
  8. var spinner;
  9. var spinnerContainer = document.getElementById('viewerContainer');
  10. // 能打印的最大页数(页数超过,需要先下载pdf,再打印)
  11. var PRINT_MAX_PAGE_SIZE = 200;
  12. var ALERT_FILE_TOO_LARGE = "PDF超过" + PRINT_MAX_PAGE_SIZE
  13. + "页,建议先下载到本地,再进行查看或打印";
  14. var pdfDoc;
  15. // 页码
  16. var pageIndex;
  17. // 总页数
  18. var pageSize;
  19. // 预览的pdf的缩放级别(为pdf原大小的倍数)
  20. var scale;
  21. // 浏览器窗口高度
  22. var winHeight;
  23. // 浏览器窗口宽度
  24. var winWidth;
  25. // 将要打印的总的pdf(整个文档,而不是某一页的pdf)相对路径
  26. var wholePdfPath;
  27. // 某一页pdf文件的路径
  28. var pagedPdfPath;
  29. // 参数打印类型,可能为PRINT、PREVIEW
  30. var printType = getParameter("printType");
  31. // 首次加载页面()
  32. var firstRequest = true;
  33. // 记录刚加载该页面时的时间
  34. var startTime = new Date();
  35. // 记录查询完整pdf状态的次数
  36. var waitWholePdfGeneratedCount = 0;
  37. // 页面过期时间为10分钟,之后必须重新加载
  38. var MAX_TIME = 10 * 60 * 1000;
  39. // 查询总的pdf状态的次数最高为100次,超过后需要重新加载页面
  40. var MAX_WAIT_WHOLE_PDF_GENERATED = 100;
  41. var ALERT_TIMEOUT = "页面已过期,请刷新页面!";
  42. var ALERT_WAIT_WHOLE_PDF_GENERATED_TOO_LARGE = "PDF加载错误,请刷新页面!";
  43. getWindowWidth();
  44. loadData(1);
  45. // 缩小,最小不小于原大小的0.2/1.2倍
  46. $("#zoomOut").click(function() {
  47. if (scale >= 0.2) {
  48. scale = scale / 1.2;
  49. changeTextOfSelectScale()
  50. renderPage();
  51. }
  52. });
  53. // 放大,最大不大于原大小的5*1.2倍
  54. $("#zoomIn").click(function() {
  55. if (scale <= 5) {
  56. scale = scale * 1.2;
  57. changeTextOfSelectScale()
  58. renderPage();
  59. }
  60. });
  61. // 选择缩放倍数
  62. $("#scaleSelect").change(function() {
  63. scale = this.value;
  64. renderPage();
  65. });
  66. // 上页
  67. $("#prev").click(prevPage);
  68. // 下页
  69. $("#next").click(nextPage);
  70. // 手动输入页码
  71. $("#pageIndex").keypress(function(event) {
  72. if (!pdfDoc) {
  73. return;
  74. }
  75. // 按Enter键
  76. if (event.keyCode == 13) {
  77. var value = document.getElementById("pageIndex").value;
  78. // 以非0开头的整数
  79. var regExp = /^([1-9]+\d*)$/;
  80. if (regExp.test(value) && value >= 1 && value <= pageSize) {
  81. if (value == pageIndex) {
  82. return;
  83. }
  84. if (pageSize > PRINT_MAX_PAGE_SIZE) {
  85. alert(ALERT_FILE_TOO_LARGE);
  86. return;
  87. }
  88. pageIndex = value;
  89. if (timeout()) {
  90. return;
  91. }
  92. loadData(pageIndex);
  93. } else {
  94. document.getElementById("pageIndex").value = pageIndex;
  95. }
  96. }
  97. });
  98. // 打印
  99. $("#print").click(function() {
  100. if (!pdfDoc) {
  101. return;
  102. }
  103. printPdf();
  104. });
  105. // 下载pdf
  106. $("#downloadPdf").click(
  107. function() {
  108. if (!pdfDoc) {
  109. return;
  110. }
  111. if (timeout()) {
  112. return;
  113. }
  114. console.log(new Date().format()
  115. + " ---- subscribed wholePdfGeneratedSignal");
  116. $.subscribe("wholePdfGeneratedSignal", downloadPdf);
  117. spinner = showLoading(spinner, spinnerContainer);
  118. waitWholePdfGenerated();
  119. });
  120. /**
  121. * 下载文件
  122. */
  123. function downloadPdf() {
  124. spinner = hideLoading(spinner);
  125. console.log(new Date().format()
  126. + " ---- received and unsubscribe wholePdfGeneratedSignal");
  127. $.unsubscribe("wholePdfGeneratedSignal", downloadPdf);
  128. console.log(new Date().format() + " ---- start download...");
  129. window.location = downloadUrl("pdf");
  130. hiddenFrame.src = wholePdfPath;
  131. }
  132. // 下载纯数据excel
  133. $("#downloadExcel").click(function() {
  134. if (!pdfDoc) {
  135. return;
  136. }
  137. window.open(downloadUrl("xls"));
  138. });
  139. // 下载纯数据excel
  140. $("#downloadExcelWithOnlyData").click(function() {
  141. if (!pdfDoc) {
  142. return;
  143. }
  144. window.open(downloadUrl("xls_with_only_data"));
  145. });
  146. // 键盘左右键进行翻页
  147. $("body").keydown(function(event) {
  148. // 如果在选中input输入框或select下拉列表时按左右键,不进行翻页
  149. var activeElementNodeName = document.activeElement.nodeName.toLowerCase();
  150. if (activeElementNodeName == "input" || activeElementNodeName == "select") {
  151. return;
  152. }
  153. if (event.keyCode == 37) {// left
  154. prevPage();
  155. } else if (event.keyCode == 39) {// right
  156. nextPage();
  157. }
  158. });
  159. /**
  160. * 获取窗口宽度
  161. */
  162. function getWindowWidth() {
  163. if (window.innerWidth)
  164. winWidth = window.innerWidth;
  165. else if ((document.body) && (document.body.clientWidth))
  166. winWidth = document.body.clientWidth;
  167. // 获取窗口高度
  168. if (window.innerHeight)
  169. winHeight = window.innerHeight;
  170. else if ((document.body) && (document.body.clientHeight))
  171. winHeight = document.body.clientHeight;
  172. // 通过深入 Document 内部对 body 进行检测,获取窗口大小
  173. if (document.documentElement && document.documentElement.clientHeight
  174. && document.documentElement.clientWidth) {
  175. winHeight = document.documentElement.clientHeight;
  176. winWidth = document.documentElement.clientWidth;
  177. }
  178. }
  179. /**
  180. * 打印
  181. */
  182. function printPdf() {
  183. spinner = hideLoading(spinner);
  184. if (pageSize > PRINT_MAX_PAGE_SIZE) {
  185. alert(ALERT_FILE_TOO_LARGE);
  186. return;
  187. }
  188. if (!pdfDoc) {
  189. return;
  190. }
  191. if (timeout()) {
  192. return;
  193. }
  194. console.log(new Date().format()
  195. + " ---- subscribed wholePdfGeneratedSignal");
  196. $.subscribe("wholePdfGeneratedSignal", print);
  197. spinner = showLoading(spinner, spinnerContainer);
  198. waitWholePdfGenerated();
  199. }
  200. /**
  201. * 取消订阅信号,打印
  202. */
  203. function print() {
  204. console.log(new Date().format()
  205. + " ---- received and unsubscribe wholePdfGeneratedSignal");
  206. $.unsubscribe("wholePdfGeneratedSignal", print);
  207. console.log(new Date().format() + " ---- start print...");
  208. checkBrowser();
  209. setTimeout(
  210. "hiddenFrame.contentWindow.print();spinner = hideLoading(spinner)",
  211. 1000);
  212. }
  213. /**
  214. * 获取预览的pdf文档数据,完成后加载整个文档
  215. *
  216. * @param pagedPdfPath
  217. * 需要进行预览的分页pdf相对路径
  218. * @param ifPreloadWholePdf
  219. * 渲染之后,是否预加载整个文档
  220. */
  221. function loadPagedPdf(pagedPdfPath, ifPreloadWholePdf) {
  222. // var dfd = $.Deferred();
  223. PDFJS
  224. .getDocument(pagedPdfPath)
  225. .then(
  226. function(pdfDoc_) {
  227. // 更新页码
  228. document.getElementById('pageIndex').value = pageIndex;
  229. pdfDoc = pdfDoc_;
  230. // 第一页文档渲染完成后,再加载整个文档
  231. if (ifPreloadWholePdf) {
  232. if (printType == 'PRINT'
  233. && pageSize > PRINT_MAX_PAGE_SIZE) {
  234. alert(ALERT_FILE_TOO_LARGE);
  235. spinner = hideLoading(spinner);
  236. } else {
  237. console
  238. .log(new Date().format()
  239. + " ---- subscribed wholePdfGeneratedSignal");
  240. $.subscribe("wholePdfGeneratedSignal",
  241. loadWholePdf);
  242. spinner = showLoading(spinner, spinnerContainer);
  243. waitWholePdfGenerated();
  244. }
  245. }
  246. // Initial/first page rendering
  247. renderPage();
  248. // $.when(rend("getDocument promised");
  249. // dfd.resolve();
  250. // });
  251. });
  252. // return dfd.promise;
  253. }
  254. /**
  255. * 发送请求,服务器端进行填充报表、生成pdf文件等操作
  256. */
  257. function loadData(page) {
  258. ctx.clearRect(0, 0, canvas.width, canvas.height);
  259. spinner = showLoading(spinner, spinnerContainer);
  260. pageIndex = page || 1;
  261. var loadPdfDataUrl = "print/loadPdfData" + window.location.search;
  262. loadPdfDataUrl = loadPdfDataUrl + "&pageIndex=" + pageIndex;
  263. $.ajax({
  264. type : "get",
  265. async : false,
  266. url : loadPdfDataUrl,
  267. success : function(data) {
  268. // 返回的pdf文件路径
  269. wholePdfPath = data.pdfPath;
  270. // 第pageIndex页的pdf文件路径
  271. pagedPdfPath = wholePdfPath.replace(".pdf", "_" + pageIndex
  272. + ".pdf");
  273. if (firstRequest) {
  274. pageSize = data.pageSize;
  275. document.getElementById('pageSize').textContent = pageSize;
  276. document.title = getParameter("reportName");
  277. // 加载第一页文档,并且预加载整个文档
  278. loadPagedPdf(pagedPdfPath, true);
  279. firstRequest = false;
  280. // $.when(getDocument()).done(loadWholePdf);
  281. // console.log(renderTask._internalRenderTask.running);
  282. } else {
  283. loadPagedPdf(pagedPdfPath);
  284. }
  285. },
  286. error : function(XMLHttpRequest) {
  287. $("#theCanvas").remove();
  288. spinner = hideLoading(spinner);
  289. $("#errorMessageContainer").removeAttr("hidden");
  290. // 处理后台传输的自定义的换行标志
  291. var result = JSON.parse(XMLHttpRequest.responseText);
  292. var message = result.message;
  293. $("#message").html(message);
  294. if (result.detailedMessage) {
  295. $("#detailedMessageButton").removeAttr("hidden");
  296. }
  297. $("#detailedMessageButton").click(function() {
  298. $("#detailedMessage").html(result.detailedMessage);
  299. $("#detailedMessage").removeAttr("hidden");
  300. });
  301. }
  302. });
  303. };
  304. /**
  305. * 预加载整个pdf(大于PRINT_MAX_PAGE_SIZE页,不加载)文件,以提高后续打印速度
  306. */
  307. function loadWholePdf() {
  308. console.log(new Date().format()
  309. + " ---- received and unsubscribe wholePdfGeneratedSignal");
  310. $.unsubscribe("wholePdfGeneratedSignal", loadWholePdf);
  311. // 开始加载
  312. console.log(new Date().format() + " ---- hiddenFrame loading...");
  313. hiddenFrame.src = wholePdfPath;
  314. if (printType == "PRINT") {
  315. setTimeout("printPdf()", 1000);
  316. }
  317. }
  318. /**
  319. * 每隔一定时间查询文件状态,直到文件有效
  320. */
  321. function waitWholePdfGenerated() {
  322. waitWholePdfGeneratedCount++;
  323. if (waitWholePdfGeneratedCount >= MAX_WAIT_WHOLE_PDF_GENERATED) {
  324. alert(ALERT_WAIT_WHOLE_PDF_GENERATED_TOO_LARGE);
  325. window.location.reload();
  326. return;
  327. }
  328. var valid = getGeneratedPdfOrXlsInformation("pdf").valid;
  329. if (!valid) {
  330. console.log(new Date().format() + " ---- 文件还未生成");
  331. console.log(new Date().format() + " ---- wait 1000ms")
  332. setTimeout("waitWholePdfGenerated()", 1000);
  333. } else {
  334. console.log(new Date().format() + " ---- 文件已生成");
  335. console.log(new Date().format()
  336. + " ---- published wholePdfGeneratedSignal");
  337. $.publish("wholePdfGeneratedSignal", waitWholePdfGenerated);
  338. }
  339. }
  340. /**
  341. * 获取生成的pdf或者xls的信息
  342. *
  343. * @param fileType
  344. * 文件格式,pdf、xls、xls_with_only_data
  345. */
  346. function getGeneratedPdfOrXlsInformation(fileType) {
  347. var data;
  348. $.ajax({
  349. type : "get",
  350. async : false,
  351. url : "print/getGeneratedPdfOrXlsInformation" + window.location.search
  352. + "&fileType=" + fileType,
  353. success : function(result) {
  354. console
  355. .log(new Date().format() + " ---- "
  356. + JSON.stringify(result));
  357. data = result;
  358. }
  359. });
  360. return data;
  361. }
  362. /**
  363. * Get page info from document, resize canvas accordingly, and render page
  364. */
  365. function renderPage() {
  366. spinner = hideLoading(spinner);
  367. if (firstRequest && printType == "PRINT" && pageSize <= PRINT_MAX_PAGE_SIZE) {
  368. spinner = showLoading(spinner, spinnerContainer);
  369. }
  370. if (!pdfDoc) {
  371. return;
  372. }
  373. // 每个pdf只有一页
  374. pdfDoc
  375. .getPage(1)
  376. .then(
  377. function(page) {
  378. if (!scale || scale == "auto") {
  379. // 调整为适合的宽度
  380. scale = getScale(page, 0.75);
  381. } else if (scale == "page_width") {
  382. // 调整pdf显示的宽度接近窗口宽度
  383. scale = getScale(page, 0.95);
  384. }
  385. var viewport = page.getViewport(scale);
  386. canvas.height = viewport.height;
  387. canvas.width = viewport.width;
  388. // Render PDF page into canvas context
  389. var renderContext = {
  390. canvasContext : ctx,
  391. viewport : viewport
  392. };
  393. // 开始渲染
  394. var renderTask = page.render(renderContext);
  395. $
  396. .when(renderTask)
  397. .done(
  398. function() {
  399. // 渲染完成后,发布信号
  400. // "renderTaskFinishedSignal"
  401. console
  402. .log(new Date().format()
  403. + " ---- renderTask finished");
  404. console
  405. .log(new Date().format()
  406. + " ---- publish renderTaskFinishedSignal");
  407. $
  408. .publish("renderTaskFinishedSignal");
  409. // var renderTaskRunning =
  410. // renderTask._internalRenderTask.running;
  411. // return !renderTaskRunning;
  412. });
  413. });
  414. }
  415. /**
  416. * 获取缩放级别(将pdf显示的宽度调整为窗口宽度的multipleOfWindowWidth倍,获取该情况下pdf宽度为原宽度的倍数)
  417. *
  418. * @param page
  419. * pdf数据
  420. * @param multipleOfWindowWidth
  421. * 窗口宽度的倍数
  422. * @returns {Number} 缩放级别(pdf原宽度的倍数)
  423. */
  424. function getScale(page, multipleOfWindowWidth) {
  425. // 首先获取pdf原始宽度
  426. var viewportWidth = page.getViewport(1).width;
  427. return multipleOfWindowWidth / (viewportWidth / winWidth);
  428. }
  429. /**
  430. * 预览前一页
  431. */
  432. function prevPage() {
  433. // 验证pdfDoc不存在,是为了避免报表出现编译失败等问题时,仍然试图翻页
  434. if (!pdfDoc || pageIndex <= 1) {
  435. return;
  436. }
  437. if (timeout()) {
  438. return;
  439. }
  440. // 获取前一页的pdf
  441. pageIndex--;
  442. loadData(pageIndex);
  443. }
  444. /**
  445. * 预览后一页
  446. */
  447. function nextPage() {
  448. if (!pdfDoc || pageIndex >= pageSize) {
  449. return;
  450. }
  451. if (pageSize > PRINT_MAX_PAGE_SIZE) {
  452. alert(ALERT_FILE_TOO_LARGE);
  453. return;
  454. }
  455. if (timeout()) {
  456. return;
  457. }
  458. pageIndex++;
  459. loadData(pageIndex);
  460. }
  461. /**
  462. * 修改缩放下拉框所显示的内容
  463. */
  464. function changeTextOfSelectScale() {
  465. var hiddenOption = document.getElementById("hiddenOption");
  466. hiddenOption.removeAttribute("hidden");
  467. hiddenOption.text = (scale * 100).toFixed() + "%";
  468. hiddenOption.selected = true;
  469. hiddenOption.hidden = true;
  470. }
  471. /**
  472. * 下载报表
  473. *
  474. * @param exportFileType
  475. * 导出文件的格式
  476. * @returns {String} 下载链接
  477. */
  478. function downloadUrl(exportFileType) {
  479. var downloadUrl = "print/export" + window.location.search;
  480. exportFileType = exportFileType || "pdf";
  481. // 导出文件的格式
  482. downloadUrl += "&exportFileType=" + exportFileType;
  483. return downloadUrl;
  484. }
  485. /**
  486. * 检查浏览器并提示
  487. */
  488. function checkBrowser() {
  489. var userAgent = navigator.userAgent;
  490. console.log(userAgent);
  491. if (userAgent.indexOf("Chrome") == -1 || userAgent.indexOf("Edge") !== -1) {
  492. alert("建议使用最新版Chrome浏览器进行打印");
  493. }
  494. }
  495. /**
  496. * 检查页面是否已过期
  497. *
  498. * @returns {Boolean} true,如果已过期
  499. */
  500. function timeout() {
  501. var now = new Date();
  502. if (now - startTime >= MAX_TIME) {
  503. alert(ALERT_TIMEOUT);
  504. window.location.reload();
  505. return true;
  506. }
  507. return false;
  508. }