| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- <template>
- <div class="page_dialog">
- <!-- v-model="previewVisible" -->
- <el-dialog
- @close="HandleDialogClose"
- class="preview_dialog el_button"
- :model-value="props.previewVisible"
- style="max-height: 90vh;overflow: auto;max-width: 900px;"
- :width="dialogWidth ? dialogWidth : '80%'"
- :title="props.title || '附件预览'"
- append-to-body
- :align-center="true"
- >
- <div class="custom">
- <!-- 内部 class="preview_introduce" -->
- <slot></slot>
- </div>
- <div v-if="filePreviewList.includes(previewType)" class="preview_item">
- <VuePdfEmbed
- class="preview_main"
- v-if="previewType == 'pdf'"
- annotation-layer
- :scale="1"
- text-layer
- :source="fileUrl"
- />
- <vue-office-excel
- v-else-if="previewType == 'xlsx'"
- :src="fileUrl"
- :options="excelOptions"
- style="height: 100vh;"
- @rendered="renderedHandler"
- @error="errorHandler"
- />
- <vue-office-docx
- class="preview_main"
- v-else-if="previewType == 'docx'"
- :src="fileUrl"
- @rendered="renderedHandler"
- @error="errorHandler"
- />
- <video class="preview_main" controls width="100%" v-else-if="['mp4','avi','mpg','mpeg','wmv','mkv','mov','flv','mpg'].includes(previewType)" :src="fileUrl" />
-
- <!-- <iframe
- v-else-if="['ppt','pptx'].includes(previewType)"
- :src="`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(fileUrl)}`"
- width="100%"
- height="600px"
- frameborder="0"
- ></iframe> -->
- <div
- v-else-if="['pptx'].includes(previewType)"
- class="vue_ppt" id="vue_ppt" ref="vuePpt"
- >
-
- <div v-if="pptLoading" class="loading_bg">
- <el-icon class="is-loading">
- <Loading />
- </el-icon>
- 加载中
- </div>
- <!-- ppt加载中 -->
- </div>
- <img @click="HandleImgPreview" style="text-align: center;" v-else-if="['jpg','png','jpeg','gif','svg','webp'].includes(previewType)" :src="fileUrl"/>
- </div>
- <div class="preview_item" v-else-if="fileName && fileUrl">
- <span>不支持预览,请点击下载: <a :href="fileUrl">{{ fileName }}</a></span>
- </div>
-
- <template v-if="props.customFooter" #footer>
- <el-button @click="HandleDialogClose" class="button_border_gray cancel" >取 消</el-button>
- <slot name="footer"></slot>
-
- <slot name="confirm">
- <el-button :disabled="props.disabledConfirm" :loading="props.submitLoading" @click="props.disabledConfirm ? '' : HandleConfirmCheck()" class="button_background" >确 定</el-button>
- </slot>
- </template>
- </el-dialog>
- <el-image-viewer
- v-if="imgVisible"
- :url-list="[fileUrl]"
- show-progress
- @close="imgVisible = false"
- />
- </div>
- </template>
- <script setup lang="ts">
- import {init} from 'pptx-preview'
- import { watch ,ref } from 'vue'
- import VuePdfEmbed from 'vue-pdf-embed'
- // optional styles
- import 'vue-pdf-embed/dist/styles/annotationLayer.css'
- import 'vue-pdf-embed/dist/styles/textLayer.css'
- //引入VueOfficeExcel组件
- import VueOfficeExcel from '@vue-office/excel'
- //引入相关样式
- import '@vue-office/excel/lib/index.css'
- //引入VueOfficeDocx组件
- import VueOfficeDocx from '@vue-office/docx'
- //引入相关样式
- import '@vue-office/docx/lib/index.css'
- interface FileMsg {
- name:String,
- url:String
- }
- const props = defineProps({
- fileMessage:{
- required:true,
- type:Object
- },
- title:{
- type:String
- },
- customFooter:{
- type:Boolean,
- required:false
- },
- previewVisible:{
- type:Boolean,
- required:true
- },
- submitLoading:{
- type:Boolean,
- default:false,
- },
- disabledConfirm:{
- type:Boolean,
- default:false
- },
- dialogWidth:{
- type:String,
- }
- })
- const emit = defineEmits(['close','confirm'])
- const pptxPrviewer = ref()
- const vuePpt = ref()
- const pptLoading = ref(false)
- const fileUrl = ref()
- const fileName = ref('')
- // const previewVisible = ref(false)
- const previewType = ref('')
- const imgVisible = ref(false)
- const excelOptions = {
- xls: false, //预览xlsx文件设为false;预览xls文件设为true
- minColLength: 0, // excel最少渲染多少列,如果想实现xlsx文件内容有几列,就渲染几列,可以将此值设置为0.
- minRowLength: 0, // excel最少渲染多少行,如果想实现根据xlsx实际函数渲染,可以将此值设置为0.
- widthOffset: 10, //如果渲染出来的结果感觉单元格宽度不够,可以在默认渲染的列表宽度上再加 Npx宽
- heightOffset: 10, //在默认渲染的列表高度上再加 Npx高
- beforeTransformData: (workbookData) => {return workbookData}, //底层通过exceljs获取excel文件内容,通过该钩子函数,可以对获取的excel文件内容进行修改,比如某个单元格的数据显示不正确,可以在此自行修改每个单元格的value值。
- transformData: (workbookData) => {return workbookData}, //将获取到的excel数据进行处理之后且渲染到页面之前,可通过transformData对即将渲染的数据及样式进行修改,此时每个单元格的text值就是即将渲染到页面上的内容
- }
- const renderedHandler = ()=> {
- console.log("渲染完成")
- }
- const errorHandler = () => {
- console.log("渲染失败")
- }
- const HandleImgPreview = ()=>{
- imgVisible.value = true
- }
- const HandleDialogClose = ()=>{
- emit('close')
- console.log('弹窗关闭===')
- // previewVisible.value = false
- fileUrl.value = ''
- fileName.value = ''
- previewType.value = ''
- }
- const HandleConfirmCheck = async () => {
- emit('confirm')
- }
- const HandlePptPreview =(url)=>{
- console.log('初始化ppt中')
- if(url && vuePpt.value){
- pptLoading.value = true
- //调用库的init方法生成一个预览器
- pptxPrviewer.value = init(vuePpt.value, {
- // width: (vuePpt.value?.clientWidth * 0.8),
- width: (vuePpt.value?.clientWidth),
- // height: 540
- })
- //获取文件或者读取文件,获取文件的 ArrayBuffer格式数据,传给组件进行预览
- fetch(props?.fileMessage?.url).then(response=>{
- return response.arrayBuffer()
- }).then(res =>{
- console.log('获取文件完成',res)
- // 在这里监听文档的滚动
- let pptDom = document.querySelectorAll('.vue_ppt')
- console.log('dom',pptDom)
- //调用预览器的preview方法
- pptxPrviewer.value.preview(res).then(previewRes=>{
- // 此处数据加载成功
- console.log('转换结果',previewRes)
- pptLoading.value = false
- }).catch(err=>{
- // 解析失败
- pptLoading.value = false
- })
- }).catch(err=>{
- // 获取文件失败
- pptLoading.value = false
- })
- }
- }
- const filePreviewList = ref(['pptx','pdf','gif','jpeg','jpg','png','svg','webp','xlsx','docx','mp4','avi','mpg','mpeg','wmv','mkv','mov','flv','mpg'])
- watch(()=>props.fileMessage,(newVal)=>{
- // previewVisible.value = true
- let suffixName = newVal?.name?.split('.').pop()
- if(suffixName){
- fileName.value = newVal.name
- fileUrl.value = newVal.url
- if(filePreviewList.value.includes(suffixName)){
- previewType.value = suffixName
- }
- /*
- else{
- let link = document.createElement('a')
- link.setAttribute('href',newVal.url)
- document.body.appendChild(link)
- link.click()
- console.log('link',link)
- document.body.removeChild(link)
- }
- */
- }
- })
- watch(()=>vuePpt.value,(newVal)=>{
- if(newVal){
- HandlePptPreview(props.fileMessage?.url)
- }
- })
- </script>
- <style lang="scss" scoped>
- .preview_item{
- background-color: #f0f4f8;
- border-radius: 8px;
- padding:10px;
-
- overflow: auto;
- display: flex;
- flex-direction: column;
- .preview_main{
- height: 100%;
- overflow: auto;
- border-radius: 8px;
- width: 100% !important;
- }
- }
- .vue_ppt{
- display: flex;
- position: relative;
- flex-direction: column;
- align-items: center;
- justify-content: flex-start;
- min-height: 270px;
- // min-height: 100%;
- overflow: auto;
- background-color: #e8eaee;
- .loading_bg{
- width: 100%;
- flex: 1 1;
- display: flex;
- justify-content: center;
- align-items: center;
- gap:3px;
- color:#2e64fa;
- }
- }
- </style>
- <style lang="scss">
- .preview_dialog{
- overflow: auto;
- border-radius: 8px;
- padding: 0;
- .el-dialog__headerbtn:focus .el-dialog__close,
- .el-dialog__headerbtn:hover .el-dialog__close {
- color: #999;
- }
- .el-dialog__header {
- width: 100%;
- height: 48px;
- line-height: 48px;
- text-indent: 20px;
- background-color: #f5f7fa;
- border-bottom: 1px solid #f5f7fa;
- border-top-right-radius: 8px;
- border-top-left-radius: 8px;
- padding: 0 !important;
- text-align: left;
- .el-dialog__title {
- font-weight: 600;
- font-size: 16px;
- color: #303133;
- }
- button:focus, button:focus-visible {
- outline: none;
- }
- }
- //饿了么内容样式覆盖
- .el-dialog__body {
- font-weight: 400;
- font-size: 16px;
- color: #333;
- padding: 20px;
- width: 100%;
- margin: auto;
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- max-height: calc(90vh - 50px - 66px);
- img{
- border-radius: 8px;
- align-self: center;
- }
- .custom{
- .preview_introduce{
- background-color: #fafafa;
- border: solid #e9e9e9 1px !important;
- padding:10px;
- margin-bottom: 10px;
- border-radius: 8px;
- display: flex;
- flex-direction: row;
- justify-content: flex-start;
- align-items: flex-start;
- }
- }
- }
- .el-dialog__footer{
- padding:15px 20px;
- border-top:solid #EBEEF5 1px;
- }
- }
- .preview_main{
- .docx-wrapper{
- // width: 100% !important;
- background-color: #f0f4f8 !important;
- overflow: auto;
- box-sizing: border-box;
- padding-top: 20px;
- padding-bottom: 20px;
- .docx{
- width: 100% !important;
- overflow: auto;
- }
- }
- .vue-pdf-embed__page{
- width: 100% !important;
- overflow: auto;
- padding:20px;
- background-color: #f0f4f8 !important;
- canvas{
- width: 100% !important;
- background-color: #f0f4f8 !important;
- }
- .textLayer{
- width: 100% !important;
- height: 100% !important;
- }
- .annotationLayer{
- width: 100% !important;
- height: 100% !important;
- }
- }
- }
- </style>
|