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

iShot_2024-10-14_16.47.10

目录结构

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!') })
      }
    }
代码使用

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud Sentinel配置

Spring Cloud Sentinel 文章目录 Spring Cloud Sentinel1. Sentinel Dashboard 启动2. Spring Cloud 客户端配置3. Sentinel Dashboard 限流配置流控模式直连关联链路 流控规则快速失败Warm Up排队等待 4. Sentinel Dashboard 熔断配置5. Sentinel Dashboard 热点配置 1. Senti…

MEMC功能详解

文章目录 MEMC的工作原理&#xff1a;优点&#xff1a;缺点&#xff1a;适用场景&#xff1a;1. Deblur&#xff08;去模糊&#xff09;2. Dejudder&#xff08;去抖动&#xff09;总结两者区别&#xff1a; MEMC&#xff08;Motion Estimation and Motion Compensation&#x…

打破“几何第五公设不可证明”的神话——黄氏平行定义使证明第五公设易如反掌

黄小宁 绿色图片中的直线平行的定义&#xff08;此定义可推广为相应的平面平行的定义&#xff09;使人能根据几何常识一下子证明第五公设从而表明“2000年都无人能解决的世界著名数学难题”其实是一个天大的笑话。

Linux 手撕线程池

前言 线程池 是 池化技术 中很典型的一个&#xff0c;它旨在高效的管理和复用线程资源&#xff01;在现在的计算机体系中&#xff0c;线程是执行任务&#xff08;调度&#xff09;的基本单位。然而&#xff0c;频繁的创建和销毁线程也会带来较大的开销&#xff0c;包括系统资源…

RISC-V笔记——Pipeline依赖

1. 前言 RISC-V的RVWMO模型主要包含了preserved program order、load value axiom、atomicity axiom、progress axiom和I/O Ordering。今天主要记录下preserved program order(保留程序顺序)中的Pipeline Dependencies(Pipeline依赖)。 2. Pipeline依赖 Pipeline依赖指的是&a…

ECCV‘24 | WTConv:小参数大感受野,基于小波变换的新型卷积

前言 近年来&#xff0c;人们尝试增加卷积神经网络&#xff08;CNN&#xff09;的卷积核大小&#xff0c;以模拟视觉Transformer&#xff08;ViTs&#xff09;自注意力模块的全局感受野。然而&#xff0c;这种方法很快就遇到了上限&#xff0c;并在实现全局感受野之前就达到了饱…

鸿蒙原生应用扬帆起航

就在2024年6月21日华为在开发者大会上发布了全新操作的系统HarmonyOS Next开发测试版&#xff0c;网友们把它称之为“称之为纯血鸿蒙”。因为在此之前鸿蒙系统底层式有两套基础架构的&#xff0c;一套是是Android的AOSP&#xff0c;一套是鸿蒙的Open Harmony&#xff0c;因为早…

一篇文章教你完成软件验收测试,项目结题不再难

在软件开发过程中&#xff0c;验收测试是项目结题前的最后一道关卡。能否顺利通过验收测试&#xff0c;直接关系到项目的成功与否。 了解软件验收测试的重要性 软件验收测试是项目开发周期中的关键环节&#xff0c;其主要目的是检验软件是否满足用户需求、设计规范和合同要求…

C Primer Plus 第9章——第一篇

你该逆袭了 文章目录 一、复习函数1、定义带形式参数的函数2、声明带形式参数函数的原型3、使用 return 从函数中返回值&#xff08;1&#xff09;、返回值不仅可以赋给变量&#xff0c;也可以被用作表达式的一部分。&#xff08;2&#xff09;、返回值不一定是变量的值&#x…

机器视觉入门基础相关概念一 ——单目相机模型

机器视觉入门基础相关概念 相机模型 引言介绍&#xff1a;如果只是希望获取图像上的一些信息&#xff08;例如特征提取、拟合等&#xff09;&#xff0c;那么我们不会对三维空间中相机的位置有所要求。但如果希望通过二维的图像去理解三维空间中摄像机的信息&#xff0c;或者是…

简单三步完成 Telegram 生态的 Web3 冷启动

在竞争激烈的 Web3 领域&#xff0c;强有力的启动往往能决定成败。Telegram 无疑当下最火热的流量池&#xff0c;是很多 Web3 项目冷启动阶段的必选项。 但眼看着好多项目在 Telegram 生态火速获取百万级甚至千万级别的用户&#xff0c;自己的项目要怎么开始做增长&#xff0c;…

【记录】Django数据库的基础操作

数据库连接 在Django中使用 mysqlclient 这个包用于数据库的连接&#xff0c;切换至 Django环境中直接 pip install mysqlclient 安装此包 1 数据库连接配置 在项目目录下的setting.py中配置 DATABASES {default: {ENGINE: django.db.backends.mysql,NAME: mini,#数据库名US…

nginx过滤模块怎么生效的

在nginx中&#xff0c;如果你要开发一个过滤模块&#xff0c;config中必须要加 HTTP_FILTER_MODULES$HTTP_FILTER_MODULES xxx 否则&#xff0c;即使在postconfiguration回调中加了ngx_http_top_header_filtermy_xxxx_filter_handle&#xff0c;最终my_xxxx_filter_handle也不…

PTA L1系列题解(C语言)(L1_081 -- L1_088)

L1-081 今天我要赢 题目内容&#xff1a; 2018 年我们曾经出过一题&#xff0c;是输出“2018 我们要赢”。今年是 2022 年&#xff0c;你要输出的句子变成了“我要赢&#xff01;就在今天&#xff01;”然后以比赛当天的日期落款。 输入格式&#xff1a; 本题没有输入。 输…

聊聊ASSERT处理在某些场景下的合理用法

先看看ASSERT的介绍&#xff1a; 编写代码时&#xff0c;我们总是会做出一些假设&#xff0c;ASSERT断言就是用于在代码中捕捉这些假设&#xff0c;可以将断言看作是异常处理的一种高级形式。断言表示为一些布尔表达式&#xff0c;程序员相信在程序中的某个特定点该表达式值为真…

数据结构编程实践20讲(Python版)—20并查集

本文目录 20 并查集&#xff08;Union-Find Set&#xff09;S1 说明并查集的定义并查集基本操作并查集优化并查集特点应用领域 S2 示例S3 问题1&#xff1a;朋友圈问题S4 问题2&#xff1a;网络连接恢复问题S5 问题3&#xff1a;随机生成迷宫 往期链接 01 数组02 链表03 栈04 …

【热门】用ChatGPT做智慧农业云平台——农业ERP管控系统

随着科技的进步,原有农业种植方式已经不能满足社会发展的需要,必须对传统的农业进行技术更新和改造。经过多年的实践,人们总结出一种新的种植方法——温室农业,即“用人工设施控制环境因素,使作物获得最适宜的生长条件,从而延长生产季节,获得最佳的产出”。这种农业生产方式…

vue3中监视 Reactive对象中的属性

watch 的第一个参数可以是不同形式的“数据源”&#xff1a;它可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组 一、框架&#xff1a; <template><div class"divBox"><h2>姓名&#xff1a;{{ person.…

2024年9月中国电子学会青少年软件编程(Python)等级考试试卷(一级)答案 + 解析

一、单选题 1、下列选项中关于 turtle.color(red) 语句的作用描述正确的是&#xff1f;&#xff08; &#xff09; A. 只设置画笔的颜色为红色 B. 只设置填充的颜色为红色 C. 设置画笔和填充的颜色为红色 D. 设置画笔的颜色为红色&#xff0c;设置画布背景的颜色为红色 正…

告别ELK,APO提供基于ClickHouse开箱即用的高效日志方案——APO 0.6.0发布

ELK一直是日志领域的主流产品&#xff0c;但是ElasticSearch的成本很高&#xff0c;查询效果随着数据量的增加越来越慢。业界已经有很多公司&#xff0c;比如滴滴、B站、Uber、Cloudflare都已经使用ClickHose作为ElasticSearch的替代品&#xff0c;都取得了不错的效果&#xff…