从0到1封装一个image/pdf预览组件

news2025/1/16 21:14:49

目录结构

content.vue
<template>
  <div class="no-content-block">
    <i class="iconfont icondocument large-file" />
    <div class="text-wrapper">{{ t('__ui__.siPreview.previewSupported') }}</div>
    <div class="buttons-default" @click="donwload(url)">
      <i class="iconfont el-icon-download" />
      <div class="button-name">{{ t('__ui__.siPreview.downloadAttachment') }}</div>
    </div>
  </div>
</template>

<script>
import Locale from '../../../../mixins/locale'
export default {
  name: 'BlankContent',
  mixins: [Locale],
  inject: ['app'],
  props: {
    url: {
      type: String,
      default: ''
    }
  },
  methods: {
    donwload(url) {
      this.app.download()
      window.open(url, '_blank')
    }
  }
}
</script>
previewContent.vue
<template>
  <div class="pre-view-content">
    <div class="buttons-icons-con" @click="donwload(fileUrl)">
      <i class="iconfont el-icon-download" />
    </div>
    <div class="buttons-icons-set" @click="closeDialogue(fileType)">
      <i class="iconfont el-icon-close" />
    </div>
  </div>
</template>

<script>
export default {
  name: 'PreviewContent',
  inject: ['app'],
  props: {
    fileUrl: {
      type: String,
      default: ''
    },
    fileType: {
      type: String,
      default: ''
    }

  },
  methods: {
    donwload(url) {
      this.app.donwload(url)
    },
    closeDialogue(type) {
      this.app.closeDialogue(type)
    }
  }
}
</script>
index.js
/*
  Exposure to the outside world
    fileUrl、Other parameters;

  Transfer data
    fileUrl address;
    openLoad Yes, it's all function calls;

  Exclusive introduction
    import Vue from 'vue'
    import { FilePreview } from '@payermax/components-manage-base'

    Vue.use(FilePreview)

    (= ̄ω ̄=) file you use this component
    this.$filePreview('xxx');
  */
// import Vue from 'vue'
import _component from './index.vue'
import { prefix } from '../../../const'

const FilePreview = {
  install(Vue, options) {
    Vue.prototype.$filePreview = (url, downLoadCallback, closeCallback) => {
      // Builder
      const ComponentInstance = Vue.extend({
        render(h) {
          return h(_component, {
            props: {
              fileUrl: url,
              downloadFile: downLoadCallback,
              closeFile: closeCallback,
              openLoad: true
            }
          })
        }
      })
      // Create a singleton
      const instance = new ComponentInstance().$mount()
      document.body.appendChild(instance.$el)
    }
    Vue.component(`${prefix}${_component.name}`, _component)
  }
}

export default FilePreview

index.js 代码解释

当前代码定义了一个名为 $filePreview 的 Vue 实例方法,该方法用于在页面中预览文件。以下是对代码的详细解释:

  1. 方法定义

    javascript

    Vue.prototype.$filePreview = (url, downLoadCallback, closeCallback) => {
      //...
    }
    

    这里定义了一个 Vue 实例方法 $filePreview,它接受三个参数:url(文件的 URL 地址),downLoadCallback(下载文件的回调函数),closeCallback(关闭文件预览的回调函数)。

  2. 组件实例化

    javascript

      const ComponentInstance = Vue.extend({
        render(h) {
          return h(_component, {
            props: {
              fileUrl: url,
              downloadFile: downLoadCallback,
              closeFile: closeCallback,
              openLoad: true
            }
          })
        }
      })
    

    这里使用 Vue.extend 创建了一个 Vue 组件的子类 ComponentInstance,并在其 render 函数中使用 h 函数渲染 _component 组件,并传递了四个属性:fileUrldownloadFilecloseFileopenLoad

  3. 创建实例并挂载

    javascript

      const instance = new ComponentInstance().$mount()
      document.body.appendChild(instance.$el)
    

    这里创建了 ComponentInstance 的实例 instance,并将其挂载到一个新的 DOM 元素上,然后将这个 DOM 元素添加到文档的 body 中。

总结来说,$filePreview 方法通过创建一个 Vue 组件实例,并将其渲染到页面上,实现了文件预览的功能。用户可以通过传递文件的 URL 和相应的回调函数来使用这个方法。

index.vue
<template>
  <div ref="filePreviewContainer" class="file-preview-container">
    <div class="download-card">
      <div class="preview-wrap">
        <div v-if="openLoad" />
        <div v-else class="click-default">
          <!-- Click arera -->
          <div @click="previewFile()">
            <div v-if="!hasSlot" />
            <slot v-else />
          </div>
        </div>
        <!--  Preview types -->
        <template v-if="pdfContainerVisible && fileType == 'pdf'">
          <div>
            <div id="pdf-container" />
            <div v-loading="loading" class="pdf-loading" />
            <div class="pdf-download">
              <div class="pdf-download-container">
                <PreviewContent :file-url="fileUrl" :file-type="fileType" />
              </div>
            </div>
          </div>
        </template>
        <div v-if="imageVisible && fileType == 'image'">
          <div class="other-container">
            <div class="header">
              <PreviewContent :file-url="fileUrl" :file-type="fileType" />
            </div>
            <div class="other-containe-content">
              <img loading="lazy" alt="" :src="fileUrl">
            </div>
          </div>
        </div>
        <div v-if="otherVisible" class="other-container">
          <div class="header">
            <PreviewContent :file-url="fileUrl" :file-type="fileType" />
          </div>
          <div class="other-containe-content">
            <div v-if="openLoad" class="no-content-block">
              <i class="iconfont icondocument large-file" />
            </div>
            <BlankContent v-else :url="fileUrl" />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>

import PDFObject from 'pdfobject'
import BlankContent from './components/content.vue'
import PreviewContent from './components/previewContent.vue'

import Locale from '../../../mixins/locale'

const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg']
const pdfExtensions = ['pdf']
const SCOPE_NAME = 'FilePreview'

export default {
  name: SCOPE_NAME,
  components: {
    BlankContent,
    PreviewContent
  },
  mixins: [Locale],
  provide() {
    return {
      app: this
    }
  },
  inheritAttrs: false,
  props: {
    fileUrl: {
      type: String,
      default: ''
    },
    closeFile: {
      type: Function,
      default: () => { }
    },
    downloadFile: {
      type: Function,
      default: () => { }
    },
    openLoad: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      fileType: '',
      pdfContainerVisible: false,
      imageVisible: false,
      otherVisible: false,
      loading: false,
      hasSlot: false
    }
  },
  mounted() {
    this.init()
    this.mountPlugIn()
  },
  methods: {
    mountPlugIn() {
      if (this.openLoad) {
        this.previewFile()
      }
    },
    init() {
      this.justFileType()
      this.checkSlots()
    },
    checkSlots() {
      if (this.$slots.default) {
        this.hasSlot = true
      } else {
        this.hasSlot = false
      }
    },
    justFileType() {
      const extension = this.fileUrl.split('.').pop().toLowerCase() || ''
      if (imageExtensions.includes(extension)) {
        this.fileType = 'image'
      } else if (pdfExtensions.includes(extension)) {
        this.fileType = 'pdf'
      } else {
        this.fileType = 'other'
      }
    },
    close() {
      this.$emit('closeFile')
      this.openLoad && this.closeFile()
      this.openLoad && this.dealPluginPreviewNode()
    },
    download() {
      this.$emit('downloadFile')
      this.openLoad && this.downloadFile()
      this.openLoad && this.dealPluginPreviewNode()
    },
    dealPluginPreviewNode() {
      var containers = document.querySelectorAll('.file-preview-container')
      if (containers.length > 0) {
        var lastContainer = containers[containers.length - 1]
        var parent = lastContainer.parentNode
        parent.removeChild(lastContainer)
      }
    },
    previewFile() {
      switch (this.fileType) {
        case 'pdf':
          this.pdfPreview(this.fileUrl)
          break
        case 'image':
          this.imagePreview(this.fileUrl)
          break
        case 'other':
          this.otherPreview()
          break
        default:
          break
      }
    },
    async pdfPreview(fileUrl) {
      this.loading = true
      try {
        const response = await fetch(fileUrl)
        const status = response?.status || ''
        if (status === 200) {
          this.pdfContainerVisible = true
          this.loading = false
          this.$nextTick(() => {
            let url = ''
            if (fileUrl.startsWith('http://')) {
              url = fileUrl.substring(5)
            } else if (fileUrl.startsWith('https://')) {
              url = fileUrl.substring(6)
            } else {
              url = fileUrl
            }
            PDFObject.embed(url, '#pdf-container', {
              width: '100%'
            })
          })
        } else {
          this.loading = false
          this.otherVisible = true
        }
      } catch (error) {
        this.loading = false
        this.otherVisible = true
      }
    },
    imagePreview(fileUrl) {
      this.loading = true
      this.checkImageAccessibility(fileUrl, (accessible) => {
        if (accessible) {
          this.imageVisible = true
        } else {
          this.otherVisible = true
        }
        this.loading = false
      })
    },
    checkImageAccessibility(url, callback) {
      const img = new Image()
      img.onload = function() {
        callback(true)
      }
      img.onerror = function() {
        callback(false)
      }
      img.src = url
    },
    otherPreview() {
      this.otherVisible = true
    },
    closeDialogue(type) {
      switch (type) {
        case 'pdf':
          this.pdfContainerVisible = false
          this.close()
          break
        case 'image':
          this.imageVisible = false
          this.close()
          break
        case 'other':
          this.otherVisible = false
          this.close()
          break
        default:
          break
      }
    },
    donwload(url) {
      this.download()
      window.open(url, '_blank')
    }
  }
}
</script>
全局引入
import locale from './locale'
import { prefix } from './const'

import FilePreview from './src/Other/FilePreview'

const components = [
  FilePreview
]

export {
  FilePreview
}

function install(Vue, options = {}) {
  locale.i18n(options.i18n)

  components.forEach((component) => {
    console.log(component.name, component)
    if (component.install) {
      component.install(Vue, options)
    } else {
      Vue.component(`${prefix}${component.name}`, component)
    }
  })
}

export default install
按需引入

Text.vue

         <el-row>
            <el-col :span="6">
              <h2>图片(默认)</h2>
              <m-FilePreview
                :file-url="fileUrl"
                @closeFile="closeTest"
                @downloadFile="downloadTest"
              />
            </el-col>
            <el-col :span="6">
              <h2>其他类文件(默认)</h2>
              <m-FilePreview
                :file-url="otherFileUrl"
                @closeFile="closeTest"
                @downloadFile="downloadTest"
              />
            </el-col>
            <el-col :span="6">
              <h2>PDF文件(默认)</h2>
              <m-FilePreview
                :file-url="PdfUrl"
                @closeFile="closeTest"
                @downloadFile="downloadTest"
              />
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="6">
              <h2>图片(自定义按钮样式)</h2>
              <m-FilePreview
                :file-url="fileUrl"
                @closeFile="closeTest"
                @downloadFile="downloadTest"
              >
                <div class="haoren">
                  <el-button type="primary" plain>图片按钮</el-button>
                </div>
              </m-FilePreview>
            </el-col>
            <el-col :span="6">
              <h2>其他类文件(自定义按钮样式)</h2>
              <m-FilePreview
                :file-url="otherFileUrl"
                @closeFile="closeTest"
                @downloadFile="downloadTest"
              >
                <div class="haoren">
                  <el-button type="success" plain>无法预览的文件</el-button>
                </div>
              </m-FilePreview>
            </el-col>
            <el-col :span="6">
              <h2>PDF文件 (自定义按钮样式)</h2>
              <m-FilePreview
                :file-url="PdfUrl"
                @closeFile="closeTest"
                @downloadFile="downloadTest"
              >
                <div
                  style="color:red;
                  width:200px;
                  height: 80px;
                  background-color: aquamarine;
                  display: flex;
                  font-weight: 700;
                  justify-content: center;
                  align-items: center;
                  border-radius: 12px;
                  cursor: pointer;"
                >PDF预览演示</div>
              </m-FilePreview>
            </el-col>
          </el-row>
          <el-row style="margin-top: 80px;">
            <el-col :span="6">
              <h2>图片(函数式调用)</h2>
              <div @click="TestMess('img')">
                <el-button type="primary">函数式预览图片</el-button>
              </div>
            </el-col>
            <el-col :span="6">
              <h2>PDF (函数式调用)</h2>
              <div @click="TestMess('pdf')">
                <el-button type="info">函数式预览PDF</el-button>
              </div>
            </el-col>
            <el-col :span="6">
              <h2>其他类型</h2>
              <div @click="TestMess('other')"><button>函数式调用其他类型</button></div>
            </el-col>
          </el-row>
 fileUrl: 'https://s3.ap-s%29.gif',
 PdfUrl: 'https://s3.ap-south36596/NL2SQL.pdf',
 otherFileUrl: 'https://gimg4.baidu.com/poster/f',

  TestMess(val) {
      if (val === 'img') {
        this.$filePreview(this.fileUrl, this.downloadTest, this.closeTest)
      } else if (val === 'pdf') {
        this.$filePreview(this.PdfUrl, () => { console.log('click download!') }, () => { console.log('close window!') })
      } else {
        this.$filePreview(this.otherFileUrl, () => { console.log('click download!') }, () => { console.log('close window!') })
      }
    }
代码使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2214543.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【CSS in Depth 2 精译_049】7.2 CSS 响应式设计中的媒体查询原则(下):响应式列的添加

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 【第七章 响应式设计】&#xff08;概述&#xff09; 7.1 移动端优先设计原则&#xff08;上篇&#xff09; 7.1.1 创建移动端菜单&#xff08;下篇&#xff09;7.1.2 给视口添加 meta 标签&#xf…

RabbitMQ 核心功能详解

引言 在现代分布式系统中&#xff0c;消息队列已经成为一种不可或缺的组件。它不仅能够实现应用之间的解耦&#xff0c;还能提高系统的灵活性和可扩展性。RabbitMQ 是一款基于 AMQP&#xff08;Advanced Message Queuing Protocol&#xff09;协议的消息中间件&#xff0c;以其…

香港举办AIHCIR 2024国际学术会议,领先人工智能、人机交互和机器人技术

第三届人工智能、人机交互和机器人国际学术会议 &#xff08;AIHCIR 2024&#xff09;将于2024年11月在中国香港举行&#xff0c;聚焦AI、人机交互与机器人领域&#xff0c;邀请知名学者演讲&#xff0c;促进学术交流。论文经评审后提交EI检索&#xff0c;投稿需全英文&#xf…

图解Redis 04 | Set数据类型的原理及应用场景

介绍 Redis 的 Set 类型是一个不允许重复元素的集合&#xff0c;元素存储的顺序不按照插入的顺序&#xff0c;因此属于无序集合。一个 Set 最多可以存储 2^32 - 1 个元素&#xff0c;这与数学中的集合概念类似。Set 类型不仅支持增、删、改、查等操作&#xff0c;还支持多个Se…

微软的 Drasi:一种轻量级的事件驱动编程方法

微软的开源数据变化处理平台有望提供一种全新的方式来构建和管理可产生持续事件流的云应用程序。 Microsoft Azure 孵化团队是微软超大规模云中比较有趣的组成部分之一。它介于传统软件开发团队和研究组织之间&#xff0c;致力于构建大规模分布式系统问题的解决方案。 这些解决…

使用拖拽生成活动海报(vue项目)

<template><div class"poster-editor"><div class"toolbar" v-if"edit 1"><div style"text-align: center;font-size: 14px;font-weight: 700;margin-bottom: 10px;">工具栏</div><div class"…

WPF组件的自定义模板和触发器全面解析

Windows Presentation Foundation&#xff08;WPF&#xff09;是微软提供的一个用于构建桌面客户端应用程序的UI框架。其依赖于XAML&#xff08;Extensible Application Markup Language&#xff09;进行用户界面设计&#xff0c;提供了一套强大的控件和组件模型。在WPF开发中&…

C++ 的存储类型与新的 thread_local

1 C 的存储类型 1.1 存储周期&#xff08;Storage duration&#xff09; 存储周期表示一个变量的存储空间持续的时间&#xff0c;它应该与对象的语义生命周期一致&#xff08;或至少不小于对象的语义生命周期&#xff09;。C 98从 C 继承了三种存储周期&#xff0c;分别是静态…

【黑马点评】项目知识点及面经整理

【黑马点评】项目知识点及面经整理 1 短信登录&#xff08;Session&#xff0c;Redis&#xff0c;JWT验证&#xff09;1.1 JWT和Session的区别1.2 Session1.3 Redis1.3.1 基于Redis实现登录验证1.3.2 登录拦截 1.4 JWT1.4.1 JWT的有效验证1.4.2 JWT定义 2 热点数据缓存2.1 缓存…

区块链-智能合约Solidity编程

文章目录 一、ubuntu安装二、FISCO BCOS安装五、 WeBASE安装5.1 WeBASE简介5.2 节点前置服务搭建5.3 调用HelloWorld合约 七、Solidity极简入门7.1. 值类型7.2. 变量数据存储和作用域7.3. 函数7.4 控制流7.5 数组&映射7.6 结构体7.7 修饰符7.8 事件7.9 面向对象7.10 抽象合…

SoC芯片中Clock Gen和Reset Gen的时钟树综合

社区目前已经开设了下面列举的前四大数字后端实战课程&#xff0c;均为直播课&#xff0c;且均是小编本人亲自授课&#xff01;遇到项目问题&#xff0c;都可以远程一对一指导解决具体问题。小编本人是一线12年后端经验的数字后端工程师。想找一线IC后端技术专家亲自带你做后端…

Java Fork-Join框架学习

概述 Fork/Join是Java7提供的一个用于并行执行任务的框架&#xff0c;是一个把大任务分割成若干个小任务&#xff0c;最终汇总每个小任务结果后得到大任务结果的框架。Fork负责把一个大任务切分为若干并行执行的子任务&#xff0c;Join负责合并这些子任务的执行结果&#xff0…

Ubuntu系衍生版手动修改配置网卡的配置总结

一、Ubuntu系的IP地址配置文件的目录&#xff1a; sudo vim /etc/network/interfaces 二、以DHCP方式配置网卡&#xff1a; 在以上配置文件中添加以下两行&#xff1a; auto enp3s0 iface enp3s0 inet dhcp 三、为网卡配置静态IP地址&#xff1a; 在以上配置文件中添…

实验3,网络地址转换

实验3&#xff1a;网络地址转换 实验目的及要求&#xff1a; 通过实验&#xff0c;掌握NAT技术的工作原理&#xff0c;了解三种不同类型NAT技术的主要作用以及各自的主要应用环境。能够完成静态NAT和复用NAT技术的应用&#xff0c;并熟练掌握NAT技术相关的配置命令。 实验设…

el-date-picker选择时间后标准时间少1小时问题

问题 前端开发中发现Element的时间组件el-date-picker在选择选择部分时间后js对象的标准时间少1小时&#xff0c;如果选择的小时为0&#xff0c;会导致部分转换条件下结果少1天。 比如组件中选择的本地时间为&#xff1a; 1988-08-01 00:00:00 而js对象获取到是标准时间是&am…

ubuntu 安装kali命令补全功能

输入命令时&#xff0c;之前的命令会以阴影显示&#xff0c;按下右键或 Tab 键可以直接补全 安装zsh-autosuggestions sudo apt install zsh-autosuggestions编辑 ~/.zshrc环境变量 if [ -f /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh ]; then. /usr/share/zs…

【从零开始的LeetCode-算法】945. 使数组唯一的最小增量

给你一个整数数组 nums 。每次 move 操作将会选择任意一个满足 0 < i < nums.length 的下标 i&#xff0c;并将 nums[i] 递增 1。 返回使 nums 中的每个值都变成唯一的所需要的最少操作次数。 生成的测试用例保证答案在 32 位整数范围内。 示例 1&#xff1a; 输入&am…

【Hadoop】HDFS基本操作

参考&#xff1a;3.HDFS基本操作_哔哩哔哩_bilibili 创建目录 hadoop fs -mkdir -p /training/qiang查看当前根目录下文件 hadoop fs -ls /hadoop fs -ls /training/目录授权 hadoop fs -chmod -R 777 /training/qm777是最大权限&#xff0c;读写 4、2、1 上传文件 先创…

YZ系列工具之YZ09:VBA_Excel之读心术

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套一部VBA手册&#xff0c;教程分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的…

Python自动化脚本裁剪图片为1:1比例

一、创建输入文件夹&#xff08;in&#xff09;和输出文件夹&#xff08;out&#xff09;&#xff0c;将原始图片放在输入文件夹&#xff08;in&#xff09;里 二、 安装对应的Python库 pip install Pillow 三、编写自动化脚本代码 import os from PIL import Imagedef crop…