JEECG에서 vue-pdf와 print-js를 활용한 PDF 미리보기 및 인쇄 구현

최근 Spring Boot와 Vue2를 사용한 프론트엔드 분리 프로젝트를 개발 중이며, 국내 우수한 프레임워크인 jeecg와 ant-design-vue를 사용하고 있습니다. 프로젝트에서 파일 업로드 기능을 구현해야 했으며, 이미지와 PDF 파일을 온라인으로 미리보기하고 다운로드할 수 있어야 했습니다. 또한 페이지에서 직접 PDF 파일을 인쇄할 필요가 있었습니다. 프레임워크에 내장된 vue-print-nb-jeecg 컴포넌트는 비교적 간단하여 단일 페이지 인쇄만 지원하고 다중 페이지 인쇄는 불가능했습니다. 신중한 비교와 검토를 거쳐, 결국 vue-pdf와 print-js 컴포넌트를 사용하여 요구 사항을 충족하기로 결정했습니다.

vue-pdf는 온라인 미리보기를, print-js는 더 강력한 인쇄 기능을 제공하여 PDF, HTML, IMAGE, JSON과 같은 다양한 문서 유형을 지원합니다. 기본적으로 PDF로 설정됩니다. 사실 vue-pdf도 인쇄 기능을 구현할 수 있지만, 앞서 언급한 vue-print-nb와 마찬가지로 페이지에 표시된 첫 페이지 내용만 인쇄할 수 있습니다(미리보기 표시는 문제 없음). Print.js 공식 문서는 여기에서 확인하세요.

  1. vue 프로젝트에 vue-pdf와 Print.js 설치 yarn add vue-pdf yarn add print-js

  2. 전역으로 가져오거나 필요한 파일에서 가져오기 import pdf from 'vue-pdf' import printJS from 'print-js'

  3. 주요 코드 구현

<a-modal :visible="pdfPreviewModal" :footer="null" @cancel="closePdfPreview" :width="800">
     <div v-if="isPdfFile" style="overflow-y: auto;overflow-x: hidden;">
       <a-button shape="round" icon="file-pdf" @click="performPrint(printConfig)" size="small">인쇄</a-button>
       <div id="pdfPrintArea">
         <pdf ref="pdfViewer" v-for="pageIndex in totalPages"
              :src="currentPdfUrl"
              :key="pageIndex"
              :page="pageIndex"></pdf>
       </div>
     </div>
 </a-modal>

인쇄 버튼 실행 메서드

// 인쇄 설정 데이터
printConfig: {
  targetElementId: 'pdfPrintArea',
  documentFormat: 'html',
  headerTitle: '',
  excludedClasses: ['no-print']
},

// 인쇄 실행 메서드
performPrint(config) {
  printJS({
    printable: config.targetElementId, // 인쇄할 요소의 ID
    type: config.documentFormat || 'html',
    header: config.headerTitle, // '문서',
    targetStyles: ['*'],
    style: '@page {margin:0 10mm};', // 선택 사항 - 인쇄 시 머리글과 꼬리글 제거
    ignoreElements: config.excludedClasses || [], // ['no-print']
    properties: config.properties || null
  })
}

다른 컴포넌트에서 파일 유형에 따라 이미지는 이미지로, PDF는 PDF로 미리보기를 표시합니다.

  1. 전체 코드 예시 (핵심 로직)
<template>
  <div>
    <a-upload
      name="file"
      :multiple="true"
      :action="uploadUrl"
      :headers="authHeaders"
      :data="{'biz': businessPath}"
      :file-list="uploadedFiles"
      @change="handleFileChange"
      :list-type="'picture-card'">
      <template #placeholder>
        <a-icon type="plus" />
        <div class="ant-upload-text">파일 선택</div>
      </template>
    </a-upload>

    <a-modal :visible="pdfPreviewModal" :footer="null" @cancel="closePdfPreview" :width="800">
      <div v-if="isPdfFile" style="overflow-y: auto;overflow-x: hidden;">
        <a-button shape="round" icon="file-pdf" @click="performPrint(printConfig)" size="small">인쇄</a-button>
        <div id="pdfPrintArea">
          <pdf ref="pdfViewer" v-for="pageIndex in totalPages"
               :src="currentPdfUrl"
               :key="pageIndex"
               :page="pageIndex"></pdf>
        </div>
      </div>
    </a-modal>
  </div>
</template>

<script>
import { ACCESS_TOKEN } from "@/store/mutation-types";
import { getFileAccessHttpUrl } from '@/api/manage';
import pdf from 'vue-pdf';
import printJS from 'print-js';

export default {
  name: 'FileUploader',
  components: { pdf },
  data() {
    return {
      uploadUrl: window._CONFIG['domianURL'] + "/sys/common/upload",
      authHeaders: {},
      uploadedFiles: [],
      pdfPreviewModal: false,
      currentPdfUrl: '',
      totalPages: 0,
      isPdfFile: false,
      printConfig: {
        targetElementId: 'pdfPrintArea',
        documentFormat: 'html',
        headerTitle: '',
        excludedClasses: ['no-print']
      }
    };
  },
  created() {
    const token = Vue.ls.get(ACCESS_TOKEN);
    this.authHeaders = {"X-Access-Token": token};
  },
  methods: {
    handleFileChange(info) {
      if (info.file.status === 'done') {
        const file = info.file;
        const fileType = this.determineFileType(file.name);
        if (fileType === 'pdf') {
          this.openPdfPreview(file.url);
        }
      }
    },
    determineFileType(fileName) {
      const suffix = fileName.substr(fileName.lastIndexOf('.') + 1).toLowerCase();
      const pdfTypes = ['pdf'];
      return pdfTypes.includes(suffix) ? 'pdf' : 'other';
    },
    openPdfPreview(pdfUrl) {
      this.currentPdfUrl = pdfUrl;
      this.isPdfFile = true;
      this.pdfPreviewModal = true;
      this.fetchPdfPageCount();
    },
    closePdfPreview() {
      this.pdfPreviewModal = false;
      this.isPdfFile = false;
    },
    fetchPdfPageCount() {
      const loadingTask = pdf.createLoadingTask(this.currentPdfUrl);
      loadingTask.promise.then(pdfDoc => {
        this.totalPages = pdfDoc.numPages;
      }).catch(error => {
        console.error("PDF 페이지 수를 가져오는 중 오류 발생:", error);
      });
    },
    performPrint(config) {
      printJS({
        printable: config.targetElementId,
        type: config.documentFormat || 'html',
        header: config.headerTitle,
        targetStyles: ['*'],
        style: '@page {margin:0 10mm};',
        ignoreElements: config.excludedClasses || [],
        properties: config.properties || null
      })
    }
  }
}
</script>

결론적으로, vue-pdf와 print-js를 조합하여 PDF 미리보기와 인쇄 기능을 구현하는 방법을 살펴보았습니다. 이 외에도 바이두 개발자가 만든 vue-office 컴포넌트 라이브러리도 있으며, 장점은 사용이 간단하고 초보자에게 친화적이며 파일 주소 하나만 전달하면 미리보기를 구현할 수 있다는 점입니다. docx, excel, pdf 세 가지 일반적인 파일 미리보기를 한 번에 해결하는 종합적인 솔루션을 제공합니다. 미리보기 효과도 좋으며 콘텐츠뿐만 아니라 스타일도 지원합니다. 미리보기 효과는 정말 뛰어나지만, 인쇄 기능을 지원하지 않아 요구 사항을 충족시키지 못하는 것이 아쉽습니다. vue-office의 데모 효과는 Vue 프레임워크 데모 효과에서 확인해볼 수 있습니다.

태그: JEECG Vue.js vue-pdf print-js PDF

6월 29일 22:47에 게시됨