|
@@ -0,0 +1,193 @@
|
|
|
+<template>
|
|
|
+ <div class="preView">
|
|
|
+ <div
|
|
|
+ class="preview_box"
|
|
|
+ :style="{
|
|
|
+ width: (isFullScreen ? 100 : width) + '%',
|
|
|
+ height: (isFullScreen ? 100 : height) + '%'
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ <div class="header">
|
|
|
+ <span class="title" v-if="title">{{ title }}</span>
|
|
|
+ <div class="icon_close">
|
|
|
+ <el-icon @click="isFullScreen = !isFullScreen" style="margin-right: 15px"
|
|
|
+ ><FullScreen
|
|
|
+ /></el-icon>
|
|
|
+ <el-icon @click="closeHandler"><Close /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="content">
|
|
|
+ <template v-if="fileType === 'image'">
|
|
|
+ <img ref="imgRef" :src="props.src" />
|
|
|
+ <ul class="img_tool">
|
|
|
+ <li @click="zoomHandler(1)">
|
|
|
+ <el-icon><ZoomIn /></el-icon>
|
|
|
+ </li>
|
|
|
+ <li @click="zoomHandler(-1)">
|
|
|
+ <el-icon><ZoomOut /></el-icon>
|
|
|
+ </li>
|
|
|
+ <li @click="rotateHandler(-1)">
|
|
|
+ <el-icon><RefreshLeft /></el-icon>
|
|
|
+ </li>
|
|
|
+ <li @click="rotateHandler(1)">
|
|
|
+ <el-icon><RefreshRight /></el-icon>
|
|
|
+ </li>
|
|
|
+ <li @click="rotateHandler(0)">
|
|
|
+ <el-icon><Refresh /></el-icon>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <vue-office-pdf :src="props.src" />
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import VueOfficePdf from '@vue-office/pdf'
|
|
|
+import ImagePreview from './ImagePreview.ts'
|
|
|
+defineOptions({
|
|
|
+ name: 'PreView'
|
|
|
+})
|
|
|
+const props = withDefaults(
|
|
|
+ defineProps<{
|
|
|
+ src: string
|
|
|
+ visiable: boolean
|
|
|
+ fullScreen: boolean
|
|
|
+ width: number
|
|
|
+ height: number
|
|
|
+ title: string
|
|
|
+ }>(),
|
|
|
+ {
|
|
|
+ visiable: true,
|
|
|
+ fullScreen: false,
|
|
|
+ width: 60,
|
|
|
+ height: 90,
|
|
|
+ title: '资源预览'
|
|
|
+ }
|
|
|
+)
|
|
|
+const emit = defineEmits<{
|
|
|
+ (e: 'close')
|
|
|
+}>()
|
|
|
+const isFullScreen = ref<boolean>(false)
|
|
|
+const fileType = computed(() => {
|
|
|
+ if (!props.src) return ''
|
|
|
+ if (isPdf(props.src)) {
|
|
|
+ return 'pdf'
|
|
|
+ } else if (isPicture(props.src)) {
|
|
|
+ return 'image'
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+const isPicture = (src: string): boolean => {
|
|
|
+ if (!src) return false
|
|
|
+ const extensions: string[] = ['jpg', 'jpeg', 'png', 'gif']
|
|
|
+ const extension: string = src?.split('.')?.pop()?.toLowerCase() as string
|
|
|
+ return extensions.indexOf(extension) !== -1 ? true : false
|
|
|
+}
|
|
|
+const isPdf = (src: string): boolean => {
|
|
|
+ if (!src) return false
|
|
|
+ const extensions: string[] = ['pdf']
|
|
|
+ const extension: string = src?.split('.')?.pop()?.toLowerCase() as string
|
|
|
+ return extensions.indexOf(extension) !== -1 ? true : false
|
|
|
+}
|
|
|
+
|
|
|
+const imgRef = ref<HTMLElement>()
|
|
|
+let imagePreview: ImagePreview
|
|
|
+const zoomHandler = (num: number) => {
|
|
|
+ imagePreview.zoomHandler(num)
|
|
|
+}
|
|
|
+const rotateHandler = (num: number) => {
|
|
|
+ imagePreview.rotateHandler(num)
|
|
|
+}
|
|
|
+const closeHandler = () => {
|
|
|
+ emit('close')
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ if (fileType.value === 'image') {
|
|
|
+ imagePreview = new ImagePreview()
|
|
|
+ imagePreview.bindMouseEvent(imgRef.value as HTMLElement)
|
|
|
+ }
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.preView {
|
|
|
+ z-index: 9999999;
|
|
|
+ position: fixed;
|
|
|
+ top: 0px;
|
|
|
+ bottom: 0px;
|
|
|
+ left: 0px;
|
|
|
+ right: 0px;
|
|
|
+ background: rgba(0, 0, 0, 0.5);
|
|
|
+ > .preview_box {
|
|
|
+ background: white;
|
|
|
+ position: absolute;
|
|
|
+ left: 0px;
|
|
|
+ right: 0px;
|
|
|
+ top: 0px;
|
|
|
+ bottom: 0px;
|
|
|
+ margin: auto;
|
|
|
+ padding: 20px;
|
|
|
+ padding-top: 10px;
|
|
|
+ > .header {
|
|
|
+ height: 50px;
|
|
|
+ display: flex;
|
|
|
+ padding: 0px 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ border-bottom: 1px solid #eee;
|
|
|
+ > .title {
|
|
|
+ font-size: 20px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ > .icon_close {
|
|
|
+ font-size: 20px;
|
|
|
+ right: 20px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ > .content {
|
|
|
+ height: calc(100% - 50px);
|
|
|
+ padding: 0px 20px;
|
|
|
+ position: relative;
|
|
|
+ text-align: center;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ overflow: hidden;
|
|
|
+ > img {
|
|
|
+ display: block;
|
|
|
+ cursor: pointer;
|
|
|
+ user-drag: none;
|
|
|
+ margin-top: 8%;
|
|
|
+ user-select: none;
|
|
|
+ -moz-user-select: none;
|
|
|
+ -webkit-user-drag: none;
|
|
|
+ -webkit-user-select: none;
|
|
|
+ &.scale {
|
|
|
+ transition-duration: 0.2s;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ > .img_tool {
|
|
|
+ display: flex;
|
|
|
+ background: rgba(0, 0, 0, 0.8);
|
|
|
+ border-radius: 5px;
|
|
|
+ padding: 15px 25px;
|
|
|
+ position: absolute;
|
|
|
+ bottom: 10px;
|
|
|
+ > li {
|
|
|
+ color: #fff;
|
|
|
+ font-size: 26px;
|
|
|
+ line-height: 26px;
|
|
|
+ padding: 0px 10px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|