mirror of
				https://github.com/soybeanjs/soybean-admin.git
				synced 2025-11-04 07:43:42 +08:00 
			
		
		
		
	feat(projects): add plugin pdf preview (#568)
This commit is contained in:
		@@ -74,6 +74,7 @@
 | 
				
			|||||||
    "vue": "3.4.33",
 | 
					    "vue": "3.4.33",
 | 
				
			||||||
    "vue-draggable-plus": "0.5.2",
 | 
					    "vue-draggable-plus": "0.5.2",
 | 
				
			||||||
    "vue-i18n": "9.13.1",
 | 
					    "vue-i18n": "9.13.1",
 | 
				
			||||||
 | 
					    "vue-pdf-embed": "2.1.0",
 | 
				
			||||||
    "vue-router": "4.4.0",
 | 
					    "vue-router": "4.4.0",
 | 
				
			||||||
    "wangeditor": "4.7.15",
 | 
					    "wangeditor": "4.7.15",
 | 
				
			||||||
    "xgplayer": "3.0.19",
 | 
					    "xgplayer": "3.0.19",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -199,7 +199,8 @@ const local: App.I18n.Schema = {
 | 
				
			|||||||
    plugin_video: 'Video',
 | 
					    plugin_video: 'Video',
 | 
				
			||||||
    plugin_barcode: 'Barcode',
 | 
					    plugin_barcode: 'Barcode',
 | 
				
			||||||
    plugin_pinyin: 'pinyin',
 | 
					    plugin_pinyin: 'pinyin',
 | 
				
			||||||
    plugin_excel: 'Excel'
 | 
					    plugin_excel: 'Excel',
 | 
				
			||||||
 | 
					    plugin_pdf: 'PDF preview'
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  page: {
 | 
					  page: {
 | 
				
			||||||
    login: {
 | 
					    login: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -199,7 +199,8 @@ const local: App.I18n.Schema = {
 | 
				
			|||||||
    plugin_video: '视频',
 | 
					    plugin_video: '视频',
 | 
				
			||||||
    plugin_barcode: '条形码',
 | 
					    plugin_barcode: '条形码',
 | 
				
			||||||
    plugin_pinyin: '拼音',
 | 
					    plugin_pinyin: '拼音',
 | 
				
			||||||
    plugin_excel: 'Excel'
 | 
					    plugin_excel: 'Excel',
 | 
				
			||||||
 | 
					    plugin_pdf: 'PDF 预览'
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  page: {
 | 
					  page: {
 | 
				
			||||||
    login: {
 | 
					    login: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@ export const views: Record<LastLevelRouteKey, RouteComponent | (() => Promise<Ro
 | 
				
			|||||||
  plugin_excel: () => import("@/views/plugin/excel/index.vue"),
 | 
					  plugin_excel: () => import("@/views/plugin/excel/index.vue"),
 | 
				
			||||||
  plugin_icon: () => import("@/views/plugin/icon/index.vue"),
 | 
					  plugin_icon: () => import("@/views/plugin/icon/index.vue"),
 | 
				
			||||||
  plugin_map: () => import("@/views/plugin/map/index.vue"),
 | 
					  plugin_map: () => import("@/views/plugin/map/index.vue"),
 | 
				
			||||||
 | 
					  plugin_pdf: () => import("@/views/plugin/pdf/index.vue"),
 | 
				
			||||||
  plugin_pinyin: () => import("@/views/plugin/pinyin/index.vue"),
 | 
					  plugin_pinyin: () => import("@/views/plugin/pinyin/index.vue"),
 | 
				
			||||||
  plugin_print: () => import("@/views/plugin/print/index.vue"),
 | 
					  plugin_print: () => import("@/views/plugin/print/index.vue"),
 | 
				
			||||||
  plugin_swiper: () => import("@/views/plugin/swiper/index.vue"),
 | 
					  plugin_swiper: () => import("@/views/plugin/swiper/index.vue"),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -444,6 +444,16 @@ export const generatedRoutes: GeneratedRoute[] = [
 | 
				
			|||||||
          icon: 'mdi:map'
 | 
					          icon: 'mdi:map'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        name: 'plugin_pdf',
 | 
				
			||||||
 | 
					        path: '/plugin/pdf',
 | 
				
			||||||
 | 
					        component: 'view.plugin_pdf',
 | 
				
			||||||
 | 
					        meta: {
 | 
				
			||||||
 | 
					          title: 'plugin_pdf',
 | 
				
			||||||
 | 
					          i18nKey: 'route.plugin_pdf',
 | 
				
			||||||
 | 
					          icon:'uiw:file-pdf'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
      {
 | 
					      {
 | 
				
			||||||
        name: 'plugin_pinyin',
 | 
					        name: 'plugin_pinyin',
 | 
				
			||||||
        path: '/plugin/pinyin',
 | 
					        path: '/plugin/pinyin',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -212,6 +212,7 @@ const routeMap: RouteMap = {
 | 
				
			|||||||
  "plugin_excel": "/plugin/excel",
 | 
					  "plugin_excel": "/plugin/excel",
 | 
				
			||||||
  "plugin_icon": "/plugin/icon",
 | 
					  "plugin_icon": "/plugin/icon",
 | 
				
			||||||
  "plugin_map": "/plugin/map",
 | 
					  "plugin_map": "/plugin/map",
 | 
				
			||||||
 | 
					  "plugin_pdf": "/plugin/pdf",
 | 
				
			||||||
  "plugin_pinyin": "/plugin/pinyin",
 | 
					  "plugin_pinyin": "/plugin/pinyin",
 | 
				
			||||||
  "plugin_print": "/plugin/print",
 | 
					  "plugin_print": "/plugin/print",
 | 
				
			||||||
  "plugin_swiper": "/plugin/swiper",
 | 
					  "plugin_swiper": "/plugin/swiper",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								src/typings/components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								src/typings/components.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -19,6 +19,7 @@ declare module 'vue' {
 | 
				
			|||||||
    IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
 | 
					    IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
 | 
				
			||||||
    IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
 | 
					    IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
 | 
				
			||||||
    IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
 | 
					    IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
 | 
				
			||||||
 | 
					    'IconCharm:download': typeof import('~icons/charm/download')['default']
 | 
				
			||||||
    'IconFileIcons:microsoftExcel': typeof import('~icons/file-icons/microsoft-excel')['default']
 | 
					    'IconFileIcons:microsoftExcel': typeof import('~icons/file-icons/microsoft-excel')['default']
 | 
				
			||||||
    IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
 | 
					    IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
 | 
				
			||||||
    IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
 | 
					    IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
 | 
				
			||||||
@@ -32,6 +33,8 @@ declare module 'vue' {
 | 
				
			|||||||
    IconLocalBanner: typeof import('~icons/local/banner')['default']
 | 
					    IconLocalBanner: typeof import('~icons/local/banner')['default']
 | 
				
			||||||
    IconLocalCast: typeof import('~icons/local/cast')['default']
 | 
					    IconLocalCast: typeof import('~icons/local/cast')['default']
 | 
				
			||||||
    IconLocalLogo: typeof import('~icons/local/logo')['default']
 | 
					    IconLocalLogo: typeof import('~icons/local/logo')['default']
 | 
				
			||||||
 | 
					    'IconMaterialSymbolsLight:rotate90DegreesCcwOutlineRounded': typeof import('~icons/material-symbols-light/rotate90-degrees-ccw-outline-rounded')['default']
 | 
				
			||||||
 | 
					    'IconMdi:printer': typeof import('~icons/mdi/printer')['default']
 | 
				
			||||||
    IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
 | 
					    IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
 | 
				
			||||||
    IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
 | 
					    IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
 | 
				
			||||||
    IconMdiDrag: typeof import('~icons/mdi/drag')['default']
 | 
					    IconMdiDrag: typeof import('~icons/mdi/drag')['default']
 | 
				
			||||||
@@ -77,12 +80,14 @@ declare module 'vue' {
 | 
				
			|||||||
    NMessageProvider: typeof import('naive-ui')['NMessageProvider']
 | 
					    NMessageProvider: typeof import('naive-ui')['NMessageProvider']
 | 
				
			||||||
    NModal: typeof import('naive-ui')['NModal']
 | 
					    NModal: typeof import('naive-ui')['NModal']
 | 
				
			||||||
    NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
 | 
					    NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
 | 
				
			||||||
 | 
					    NPagination: typeof import('naive-ui')['NPagination']
 | 
				
			||||||
    NPopconfirm: typeof import('naive-ui')['NPopconfirm']
 | 
					    NPopconfirm: typeof import('naive-ui')['NPopconfirm']
 | 
				
			||||||
    NPopover: typeof import('naive-ui')['NPopover']
 | 
					    NPopover: typeof import('naive-ui')['NPopover']
 | 
				
			||||||
    NRadio: typeof import('naive-ui')['NRadio']
 | 
					    NRadio: typeof import('naive-ui')['NRadio']
 | 
				
			||||||
    NRadioGroup: typeof import('naive-ui')['NRadioGroup']
 | 
					    NRadioGroup: typeof import('naive-ui')['NRadioGroup']
 | 
				
			||||||
    NScrollbar: typeof import('naive-ui')['NScrollbar']
 | 
					    NScrollbar: typeof import('naive-ui')['NScrollbar']
 | 
				
			||||||
    NSelect: typeof import('naive-ui')['NSelect']
 | 
					    NSelect: typeof import('naive-ui')['NSelect']
 | 
				
			||||||
 | 
					    NSkeleton: typeof import('naive-ui')['NSkeleton']
 | 
				
			||||||
    NSpace: typeof import('naive-ui')['NSpace']
 | 
					    NSpace: typeof import('naive-ui')['NSpace']
 | 
				
			||||||
    NStatistic: typeof import('naive-ui')['NStatistic']
 | 
					    NStatistic: typeof import('naive-ui')['NStatistic']
 | 
				
			||||||
    NSwitch: typeof import('naive-ui')['NSwitch']
 | 
					    NSwitch: typeof import('naive-ui')['NSwitch']
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								src/typings/elegant-router.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/typings/elegant-router.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -68,6 +68,7 @@ declare module "@elegant-router/types" {
 | 
				
			|||||||
    "plugin_excel": "/plugin/excel";
 | 
					    "plugin_excel": "/plugin/excel";
 | 
				
			||||||
    "plugin_icon": "/plugin/icon";
 | 
					    "plugin_icon": "/plugin/icon";
 | 
				
			||||||
    "plugin_map": "/plugin/map";
 | 
					    "plugin_map": "/plugin/map";
 | 
				
			||||||
 | 
					    "plugin_pdf": "/plugin/pdf";
 | 
				
			||||||
    "plugin_pinyin": "/plugin/pinyin";
 | 
					    "plugin_pinyin": "/plugin/pinyin";
 | 
				
			||||||
    "plugin_print": "/plugin/print";
 | 
					    "plugin_print": "/plugin/print";
 | 
				
			||||||
    "plugin_swiper": "/plugin/swiper";
 | 
					    "plugin_swiper": "/plugin/swiper";
 | 
				
			||||||
@@ -175,6 +176,7 @@ declare module "@elegant-router/types" {
 | 
				
			|||||||
    | "plugin_excel"
 | 
					    | "plugin_excel"
 | 
				
			||||||
    | "plugin_icon"
 | 
					    | "plugin_icon"
 | 
				
			||||||
    | "plugin_map"
 | 
					    | "plugin_map"
 | 
				
			||||||
 | 
					    | "plugin_pdf"
 | 
				
			||||||
    | "plugin_pinyin"
 | 
					    | "plugin_pinyin"
 | 
				
			||||||
    | "plugin_print"
 | 
					    | "plugin_print"
 | 
				
			||||||
    | "plugin_swiper"
 | 
					    | "plugin_swiper"
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										82
									
								
								src/views/plugin/pdf/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/views/plugin/pdf/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					<script setup lang="ts">
 | 
				
			||||||
 | 
					import { ref, shallowRef } from 'vue';
 | 
				
			||||||
 | 
					import VuePdfEmbed from 'vue-pdf-embed';
 | 
				
			||||||
 | 
					import { useLoading } from '@sa/hooks';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { loading, endLoading } = useLoading(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const pdfRef = shallowRef<InstanceType<typeof VuePdfEmbed> | null>(null);
 | 
				
			||||||
 | 
					const source = `https://xiaoxian521.github.io/hyperlink/pdf/Cookie%E5%92%8CSession%E5%8C%BA%E5%88%AB%E7%94%A8%E6%B3%95.pdf`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const showAllPages = ref(false);
 | 
				
			||||||
 | 
					const currentPage = ref<undefined | number>(1);
 | 
				
			||||||
 | 
					const pageCount = ref(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function onPdfRendered() {
 | 
				
			||||||
 | 
					  endLoading();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (pdfRef.value?.doc) {
 | 
				
			||||||
 | 
					    pageCount.value = pdfRef.value.doc.numPages;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function showAllPagesChange() {
 | 
				
			||||||
 | 
					  currentPage.value = showAllPages.value ? undefined : 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const rotations = [0, 90, 180, 270];
 | 
				
			||||||
 | 
					const currentRotation = ref(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function handleRotate() {
 | 
				
			||||||
 | 
					  currentRotation.value = (currentRotation.value + 1) % 4;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function handlePrint() {
 | 
				
			||||||
 | 
					  await pdfRef.value?.print(undefined, 'test.pdf', true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function handleDownload() {
 | 
				
			||||||
 | 
					  await pdfRef.value?.download('test.pdf');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="overflow-hidden">
 | 
				
			||||||
 | 
					    <NCard title="PDF 预览" :bordered="false" class="h-full card-wrapper" content-class="overflow-hidden">
 | 
				
			||||||
 | 
					      <div class="h-full flex-col-stretch">
 | 
				
			||||||
 | 
					        <GithubLink link="https://github.com/hrynko/vue-pdf-embed" />
 | 
				
			||||||
 | 
					        <WebSiteLink label="文档地址:" link="https://www.npmjs.com/package/vue-pdf-embed" />
 | 
				
			||||||
 | 
					        <div class="flex-y-center justify-end gap-12px">
 | 
				
			||||||
 | 
					          <NCheckbox v-model:checked="showAllPages" @update:checked="showAllPagesChange">显示所有页面</NCheckbox>
 | 
				
			||||||
 | 
					          <ButtonIcon tooltip-content="旋转90度" @click="handleRotate">
 | 
				
			||||||
 | 
					            <icon-material-symbols-light:rotate-90-degrees-ccw-outline-rounded />
 | 
				
			||||||
 | 
					          </ButtonIcon>
 | 
				
			||||||
 | 
					          <ButtonIcon tooltip-content="打印" @click="handlePrint">
 | 
				
			||||||
 | 
					            <icon-mdi:printer />
 | 
				
			||||||
 | 
					          </ButtonIcon>
 | 
				
			||||||
 | 
					          <ButtonIcon tooltip-content="下载" @click="handleDownload">
 | 
				
			||||||
 | 
					            <icon-charm:download />
 | 
				
			||||||
 | 
					          </ButtonIcon>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <NScrollbar class="flex-1-hidden">
 | 
				
			||||||
 | 
					          <NSkeleton v-if="loading" size="small" class="mt-12px" text :repeat="12" />
 | 
				
			||||||
 | 
					          <VuePdfEmbed
 | 
				
			||||||
 | 
					            ref="pdfRef"
 | 
				
			||||||
 | 
					            class="overflow-auto container"
 | 
				
			||||||
 | 
					            :class="{ 'h-0': loading }"
 | 
				
			||||||
 | 
					            :rotation="rotations[currentRotation]"
 | 
				
			||||||
 | 
					            :page="currentPage"
 | 
				
			||||||
 | 
					            :source="source"
 | 
				
			||||||
 | 
					            @rendered="onPdfRendered"
 | 
				
			||||||
 | 
					          />
 | 
				
			||||||
 | 
					        </NScrollbar>
 | 
				
			||||||
 | 
					        <div class="flex-y-center justify-between">
 | 
				
			||||||
 | 
					          <div v-if="showAllPages" class="text-18px font-medium">共{{ pageCount }}页</div>
 | 
				
			||||||
 | 
					          <NPagination v-else v-model:page="currentPage" :page-count="pageCount" :page-size="1" />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </NCard>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped></style>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user