JS之文件预览功能的实现

需求:JS实现文件预览功能,可预览图片、Word文档、Excel表格、PDF文件、TXT文件。

 

Word预览:

需要安装docx-preview

 

Excel预览:

需要安装xlsx

 

PDF预览:

需要安装pdfh5

npm i pdfh5 -D

package.json

"pdfh5": "1.4.2",

package-lock.json

"node_modules/pdfh5": {
    "version": "1.4.2",
    "resolved": "https://registry.npmmirror.com/pdfh5/-/pdfh5-1.4.2.tgz",
    "integrity": "sha512-1BL8HIx/EEZowRPBgas7/WokbGEv1gxKNRmmHSimG113178mKxIBH4pxWBc0tj6d25Sy+EwnlQwv9cUUmQa42w=="
},

 

TXT预览:

txtToUtf8.js
export const txtToUtf8 = (file) => {
  return new Promise(async (resolve, reject) => {
    const codeType = await getUnicodeType(file);
    if (codeType === 'utf-8') {
      return resolve(file);
    };
    let newBlob = null
    let render = new FileReader()
    render.readAsText(file, 'gb2312')
    render.onload = (res) => {
      newBlob = new Blob([res.target.result], {
        type: "text/plain"
      })
      let newFile = new File([newBlob], file.name, {
        type: newBlob.type
      })
      resolve(newFile)
    }
    render.onerror = (err) => {
      reject(err)
    }
  })
}

// 获取txt文件编码类型
const getUnicodeType = (file) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = function (e) {
      var v8 = new Uint8Array(e.target.result);
      if (isUTF8(v8)) {
        resolve("utf-8")
      } else {
        resolve("gbk")
      }
    };
    reader.onerror = e => {
      reject(e);
    };
    reader.readAsArrayBuffer(file);
  })
}

// 判断是否为UTF8编码类型的文件
const isUTF8 = (bytes) => {
  var i = 0;
  while (i < bytes.length) {
    if (( // ASCII
      bytes[i] == 0x09 ||
      bytes[i] == 0x0A ||
      bytes[i] == 0x0D ||
      (0x20 <= bytes[i] && bytes[i] <= 0x7E)
    )) {
      i += 1;
      continue;
    }

    if (( // non-overlong 2-byte
      (0xC2 <= bytes[i] && bytes[i] <= 0xDF) &&
      (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF)
    )) {
      i += 2;
      continue;
    }

    if (( // excluding overlongs
      bytes[i] == 0xE0 &&
      (0xA0 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
      (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)
    ) ||
      ( // straight 3-byte
        ((0xE1 <= bytes[i] && bytes[i] <= 0xEC) ||
          bytes[i] == 0xEE ||
          bytes[i] == 0xEF) &&
        (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
        (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)
      ) ||
      ( // excluding surrogates
        bytes[i] == 0xED &&
        (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x9F) &&
        (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF)
      )
    ) {
      i += 3;
      continue;
    }

    if (( // planes 1-3
      bytes[i] == 0xF0 &&
      (0x90 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
      (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
      (0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
    ) ||
      ( // planes 4-15
        (0xF1 <= bytes[i] && bytes[i] <= 0xF3) &&
        (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0xBF) &&
        (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
        (0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
      ) ||
      ( // plane 16
        bytes[i] == 0xF4 &&
        (0x80 <= bytes[i + 1] && bytes[i + 1] <= 0x8F) &&
        (0x80 <= bytes[i + 2] && bytes[i + 2] <= 0xBF) &&
        (0x80 <= bytes[i + 3] && bytes[i + 3] <= 0xBF)
      )
    ) {
      i += 4;
      continue;
    }
    return false;
  }
  return true;
}

 

预览相关的全部代码示例如下:

UploadFile.vue

<template>
  <div :class="subFormItem ? 'sub-comp' : 'custom-comp'">
    <van-field
      :label="item.label"
      :required="item.isConfigRequired ?? item.isRequired"
      placeholder="点击上传"
      readonly
    >
      <template #input>
        <div class="uploader">
          <van-row>
            <van-col
              v-for="(file, index) in formData[item.prop]"
              :key="file.objectName"
              span="24"
            >
              <div class="doc-file-item">
                <img
                  class="file-icon"
                  src="@/assets/file/png-ext.png"
                  v-if="getFileType(file.fileName) === 'png'"
                />
                <img
                  class="file-icon"
                  src="@/assets/file/jpg-ext.png"
                  v-if="
                    getFileType(file.fileName) === 'jpg' ||
                    getFileType(file.fileName) === 'jpeg'
                  "
                />
                <img
                  class="file-icon"
                  src="@/assets/file/gif-ext.png"
                  v-if="getFileType(file.fileName) === 'gif'"
                />
                <img
                  class="file-icon"
                  src="@/assets/file/ppt-ext.png"
                  v-if="
                    getFileType(file.fileName) === 'ppt' ||
                    getFileType(file.fileName) === 'pptx'
                  "
                />
                <img
                  class="file-icon"
                  src="@/assets/file/pdf-ext.png"
                  v-if="getFileType(file.fileName) === 'pdf'"
                />
                <img
                  class="file-icon"
                  src="@/assets/file/word-ext.png"
                  v-if="
                    getFileType(file.fileName) === 'docx' ||
                    getFileType(file.fileName) === 'doc'
                  "
                />
                <img
                  class="file-icon"
                  src="@/assets/file/excel-ext.png"
                  v-if="
                    getFileType(file.fileName) === 'xlsx' ||
                    getFileType(file.fileName) === 'xls'
                  "
                />
                <img
                  class="file-icon"
                  src="@/assets/file/zip-ext.png"
                  v-if="getFileType(file.fileName) === 'zip'"
                />
                <img
                  class="file-icon"
                  src="@/assets/file/txt-ext.png"
                  v-if="getFileType(file.fileName) === 'txt'"
                />
                <img
                  class="file-icon"
                  src="@/assets/file/unknown-ext.png"
                  v-if="getFileType(file.fileName) === ''"
                />
                <span @click="previewFile(file)">{{ file.fileName }}</span>
                <div
                  class="file-delete"
                  @click="deleteFile(formData, index, item)"
                  v-show="!isReadOnly"
                >
                  <van-icon name="cross" size="12" />
                </div>
              </div>
            </van-col>
            <div v-if="!formData[item.prop] && isReadOnly">无附件</div>
          </van-row>
          <van-uploader
            :accept="acceptFileTypes.join(',')"
            :multiple="true"
            :preview-image="false"
            :after-read="(file) => uploaderAfter(file, 'file')"
            v-show="!isReadOnly"
          >
            <van-button icon="plus" type="primary">点击上传</van-button>
          </van-uploader>
        </div>
      </template>
    </van-field>
  </div>
  <UploadProgress
    :loading="uploadLoading"
    :rate="uploadRate"
    :show-progress="showProgress"
  />
  <van-popup
    v-model:show="previewVisible"
    round
    closeable
    position="bottom"
    :style="{ height: '90%', padding: '16px' }"
  >
    <FilePreview :file="currentFile" />
  </van-popup>
</template>

<script setup>
import {
  onMounted,
  ref,
  toRefs,
  defineAsyncComponent,
  computed,
  watch,
} from "vue";
import { Toast, Dialog } from "vant";
import ActivitiApi from "@/api/activiti";
import { checkMobileModel } from "@/utils/androidOrIos";
import {
  hasOptionsComp,
  isUploaderComp,
  PROCESS_ORDER_STATUS,
  acceptFileTypes,
  deleteFile,
  sensitiveInfoSalt,
  reportUsageRecord,
} from "@/utils/common";
import compressorImage from "@/utils/compressor";
import dayjs from "dayjs";
import UploadProgress from "@/components/uploadProgress/uploadProgress.vue";
import { useRoute, useRouter } from "vue-router";
import { txtToUtf8 } from "@/utils/txtToUtf8";

const FilePreview = defineAsyncComponent(() =>
  import("@/components/filePreview/FilePreview.vue")
);

const props = defineProps({
  formData: Object,
  item: Object,
  isReadOnly: Object,
  compList: Array,
  reportType: String,
  wholeFormData: Object,
  subFormItem: Object,
  subFormIdx: Number,
});
const {
  formData,
  item,
  isReadOnly,
  compList,
  reportType,
  wholeFormData,
  subFormItem,
  subFormIdx,
} = toRefs(props);

watch(
  () => formData.value[item.value.prop],
  (val) => {
    if (wholeFormData.value) {
      const resArr = JSON.parse(
        wholeFormData.value[subFormItem.value.prop] ?? "[]"
      );
      if (!resArr[subFormIdx.value]) {
        resArr[subFormIdx.value] = {};
      }
      resArr[subFormIdx.value][item.value.prop] = val;
      wholeFormData.value[subFormItem.value.prop] = JSON.stringify(resArr);
      sessionStorage.formState = JSON.stringify(wholeFormData.value);
    }
  }
);

const router = useRouter();
const route = useRoute();

const previewVisible = ref(false);
const currentFile = ref();
const previewFile = (file) => {
  if (
    getFileType(file.fileName) == "ppt" ||
    getFileType(file.fileName) == "pptx"
  ) {
    Toast.fail("ppt文件暂不支持预览");
  } else {
    reportDownLoad();

    const params = {
      env: "internet",
      fileName: file.objectName,
    };

    ActivitiApi.getBatchFileUrl(params)
      .then((res) => {
        const data = res.data;
        if (data.code === 200) {
          previewVisible.value = true;
          file.fileUrl = data.data;
          currentFile.value = file;
        } else {
          throw new Error(data.message);
        }
      })
      .catch((err) => {
        if (err.message) {
          Toast.fail(err.message);
        } else {
          Toast.fail("上传失败");
        }
      });
  }
};

const reportDownLoad = () => {
  const data = {
    landingPageClass: "unknown",
    landingPageSubclass: "FileDownload",
    landingPageRelateName:
      route.query.workConfCode ?? sessionStorage.workConfCode,
    quantity: 1,
    accessSource: route.query.workConfCode ? "sso" : "frontEndRoute",
  };
  if (reportType.value) {
    data.landingPageClass = reportType.value;
  }
  if (sessionStorage.eAppInfo) {
    data.landingPageClass = "eApp";
    data.landingPageRelateName = JSON.parse(
      sessionStorage.eAppInfo ?? "{}"
    ).code;
  }
  reportUsageRecord(data);
};

const uploadLoading = ref(false);
const uploadRate = ref(0);
const showProgress = ref(false);
const uploaderAfter = async (file, type) => {
  if (!file) {
    return;
  }

  uploadLoading.value = true;
  const uploadFormData = new FormData();

  if (file instanceof Array) {
    let fileList = file;

    if (type === "image") {
      fileList = await compressorImage(fileList);

      fileList.forEach((item) => {
        uploadFormData.append("fileList", item.file);
      });
    } else {
      const txts = [];
      const tasks = [];

      file.forEach((item) => {
        if (getFileType(item.file.name) === "txt") {
          txts.push(item.file);
        } else {
          uploadFormData.append("fileList", item.file);
        }
      });

      txts.forEach((txt) => {
        tasks.push(txtToUtf8(txt));
      });

      await Promise.all(tasks)
        .then((txtList) => {
          txtList.forEach((txt) => {
            uploadFormData.append("fileList", txt);
          });
        })
        .catch((err) => {
          txts.forEach((txt) => {
            uploadFormData.append("fileList", txt);
          });
        });
    }
  }

  if (file.constructor === Object) {
    if (type === "image") {
      let fileObj = file;
      fileObj = await compressorImage(fileObj);
      uploadFormData.append("fileList", fileObj.file);
    } else {
      if (getFileType(file.file.name) === "txt") {
        await txtToUtf8(file.file)
          .then((txt) => {
            uploadFormData.append("fileList", txt);
          })
          .catch((err) => {
            uploadFormData.append("fileList", file.file);
          });
      } else {
        uploadFormData.append("fileList", file.file);
      }
    }
  }

  showProgress.value = true;
  uploadFile(uploadFormData)
    .then((res) => {
      if (formData.value[item.value.prop]) {
        formData.value[item.value.prop] =
          formData.value[item.value.prop].concat(res);
      } else {
        formData.value[item.value.prop] = res;
      }
    })
    .finally(() => {
      uploadRate.value = 0;
      uploadLoading.value = false;
      showProgress.value = false;
    });
};

const uploadFile = (data) => {
  return new Promise((resolve, reject) => {
    ActivitiApi.uploadBatchFile(data, (progress) => {
      uploadRate.value = Math.round((progress.loaded / progress.total) * 100);
    })
      .then((res) => {
        const data = res.data;
        if (data.code === 200) {
          Toast.success("上传成功");
          resolve(data.data);
        } else {
          throw new Error(data.message);
        }
      })
      .catch((err) => {
        if (err.message) {
          Toast.fail(err.message);
        } else {
          Toast.fail("上传失败");
        }
        reject(err);
      });
  });
};

const uploadImage = (data) => {
  return new Promise((resolve, reject) => {
    ActivitiApi.uploadBatchImage(data, (progress) => {
      uploadRate.value = Math.round((progress.loaded / progress.total) * 100);
    })
      .then((res) => {
        const data = res.data;
        if (data.code === 200) {
          Toast.success("上传成功");
          resolve(data.data);
        } else {
          throw new Error(data.message);
        }
      })
      .catch((err) => {
        if (err.message) {
          Toast.fail(err.message);
        } else {
          Toast.fail("上传失败");
        }
        reject(err);
      });
  });
};

const openUpload = (event, data) => {
  if (data.waterMarkConfigComps?.length) {
    const result = data.waterMarkConfigComps.find((prop) => {
      return !formData.value[prop];
    });

    const target = compList.value.find((comp) => {
      return result === comp.prop;
    });

    if (target) {
      if (target.type !== "title" && target.type !== "noticeBar") {
        Toast.fail(target.label + "不能为空");
        event.preventDefault();
      }
    }
  }
};

const onOversize = (file) => {
  Toast.fail("文件大小不能超过50MB");
};

const getFileType = (fileName) => {
  if (!fileName) {
    return "";
  }
  let flag = fileName.split(".");
  return flag[flag.length - 1];
};
</script>

<style scoped lang="less">
.custom-comp {
  margin: 0px 20px;
  width: 90%;
}
.component {
  .custom-comp {
    margin: 0;
    width: 100%;
  }
}
.uploader {
  width: 100%;
  display: flex;
  flex-direction: column;
}

.doc-file-item {
  width: 100%;
  margin-bottom: 18px;
  display: flex;
  flex-shrink: 0;
  align-items: center;
}

.doc-file-item span {
  width: 100%;
  margin: 0 8px;
  font-size: 12px;
  line-height: 18px;
  word-break: break-all;
}

.file-delete {
  width: 16px;
  height: 16px;
  display: flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: #ffffff;
  background: rgba(0, 0, 0, 0.6);
}

.file-icon {
  width: 30px;
  height: auto;
}
</style>

FilePreview.vue

<script setup>
import {
  ref,
  onMounted,
  getCurrentInstance,
  reactive,
  toRefs,
  watch,
  nextTick,
} from "vue";
import * as docx from "docx-preview";
import * as XLSX from "xlsx";
import Pdfh5 from "pdfh5";
import "pdfh5/css/pdfh5.css";
import request from "../../utils/request";
import { Toast } from "vant";

const { proxy } = getCurrentInstance();
const typeName = ref("");
const imgUrl = ref("");
const srcList = ref();
const loading = ref(false);
const pdfUrl = ref("");
const pdfh5 = ref(null);
const pptUrl = ref("");
const txtUrl = ref("");
const docFile = ref(null);
const txtText = ref("");

const emits = defineEmits();
const emptyTips = ref("暂无内容");
const fullscreen = ref(false);
const data = reactive({
  excel: {
    // 数据
    workbook: {},
    // 表名称集合
    sheetNames: [],
    // 激活项
    sheetNameActive: "",
    // 当前激活表格
    SheetActiveTable: "",
  },
});
const props = defineProps({
  showTime: {
    type: Boolean,
    default: false,
  },
  file: {
    type: Object,
    default: {},
  },
  clientHeight: {
    type: Number,
    default: 600,
  },
});

const { excel } = toRefs(data);
const wordUrl = ref("");

onMounted(() => {
  loading.value = true;
  let fileType = props.file.fileName.split(".");
  fileType = fileType[fileType.length - 1];
  init(fileType);
});

// 前一个页面调用的init  我在前一个页面根据文件名字后缀已经判断是什么类型的文件了
const init = (type) => {
  typeName.value = type;
  if (
    type.startsWith("JPG") ||
    type.startsWith("JPEG") ||
    type.startsWith("PNG") ||
    type.startsWith("jpg") ||
    type.startsWith("jpeg") ||
    type.startsWith("png")
  ) {
    request({
      method: "GET",
      url: props.file.fileUrl,
      responseType: "blob", //告诉服务器想到的响应格式
      headers: {
        Accept: "application/octet-stream",
      },
    })
      .then((res) => {
        if (res) {
          let blob = new Blob([res.data], { type: "image/jpg" });
          const imageUrl = URL.createObjectURL(blob);
          imgUrl.value = imageUrl;
          (srcList.value = [imageUrl]), (loading.value = false);
        } else {
          loading.value = false;
        }
      })
      .catch(function (error) {
        console.log(error);
        loading.value = false;
      });
  } else if (type == "pdf") {
    request({
      method: "GET",
      url: props.file.fileUrl,
      responseType: "blob", //告诉服务器想到的响应格式
      headers: {
        "Content-Type": "application/octet-stream",
      },
    })
      .then((res) => {
        if (res) {
          let blob = new Blob([res.data], { type: "application/pdf" });
          const url = URL.createObjectURL(blob);
          loading.value = false;
          pdfUrl.value = url;
          pdfh5.value = new Pdfh5("#pdf-preview", {
            pdfurl: pdfUrl.value,
          });
        } else {
          loading.value = false;
        }
      })
      .catch(function (error) {
        console.log(error);
        loading.value = false;
      });
  } else if (type == "ppt" || type == "pptx") {
    pptUrl.value = "https://view.xdocin.com/view?src=" + props.file.fileUrl;
  } else if (type == "xlsx" || type == "xls") {
    //表格
    request({
      method: "GET",
      url: props.file.fileUrl,
      responseType: "arraybuffer", //告诉服务器想到的响应格式
      headers: {
        "Content-Type":
          "application/vnd.ms-excel;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      },
    })
      .then((res) => {
        console.log(res, "xls");
        loading.value = false;
        if (res) {
          const workbook = XLSX.read(new Uint8Array(res.data), {
            type: "array",
          });
          const sheetNames = workbook.SheetNames; // 工作表名称集合
          excel.value.workbook = workbook;
          excel.value.sheetNames = sheetNames;
          excel.value.sheetNameActive = sheetNames[0];
          console.log("excel", excel);
          getSheetNameTable(sheetNames[0]);
        }
      })
      .catch(function (error) {
        console.log(error);
        loading.value = false;
      });
  } else if (type == "docx" || type == "doc") {
    request({
      method: "GET",
      url: props.file.fileUrl,
      responseType: "blob", //告诉服务器想到的响应格式
    })
      .then((res) => {
        console.log("DOC", res);
        loading.value = false;
        if (res) {
          // let docx = require("docx-preview");
          docx.renderAsync(res.data, docFile.value, null, {
            className: "docx", // 默认和文档样式类的类名/前缀
            inWrapper: true, // 启用围绕文档内容渲染包装器
            ignoreWidth: false, // 禁止页面渲染宽度
            ignoreHeight: false, // 禁止页面渲染高度
            ignoreFonts: false, // 禁止字体渲染
            breakPages: true, // 在分页符上启用分页
            ignoreLastRenderedPageBreak: true, //禁用lastRenderedPageBreak元素的分页
            experimental: false, //启用实验性功能(制表符停止计算)
            trimXmlDeclaration: true, //如果为真,xml声明将在解析之前从xml文档中删除
            debug: false, // 启用额外的日志记录
          });
          // mammoth.convertToHtml({ arrayBuffer: new Uint8Array(xhr.response) }).then((resultObject) => {
          //   nextTick(() => {
          //     wordText.value = resultObject.value;
          //   });p
          // });
        }
      })
      .catch(function (error) {
        loading.value = false;
      });
  } else if (type === "txt") {
    request({
      method: "GET",
      url: props.file.fileUrl,
    })
      .then((res) => {
        txtText.value = res.data;
        loading.value = false;
      })
      .catch(function (error) {
        console.log(error);
        loading.value = false;
      });
  } else {
    Toast.fail("暂不支持预览该文件类型");
  }
};

const getSheetNameTable = (sheetName) => {
  try {
    // 获取当前工作表的数据
    const worksheet = excel.value.workbook.Sheets[sheetName];
    // 转换为数据  1.json数据有些问题,2.如果是html那么样式需修改
    let htmlData = XLSX.utils.sheet_to_html(worksheet, {
      header: "",
      footer: "",
    });
    htmlData =
      htmlData === ""
        ? htmlData
        : htmlData.replace(
            /<table/,
            '<table class="default-table" border="1px solid #ccc" cellpadding="0" cellspacing="0"'
          );
    // 第一行进行改颜色
    htmlData =
      htmlData === ""
        ? htmlData
        : htmlData.replace(/<tr/, '<tr style="background:#b4c9e8"');
    excel.value.SheetActiveTable = htmlData;
  } catch (e) {
    // 如果工作表没有数据则到这里来处理
    excel.value.SheetActiveTable =
      '<h4 style="text-align: center">' + emptyTips.value + "</h4>";
  }
};

watch(
  () => props.file,
  (newVal, oldVal) => {
    loading.value = true;
    let fileType = props.file.fileName.split(".");
    fileType = fileType[fileType.length - 1];
    console.log(props.file, "watch");
    imgUrl.value = "";
    pdfUrl.value = "";
    init(fileType);
  }
);

defineExpose({
  init,
});
</script>

<template>
  <div class="viewItemFile">
    <van-loading v-if="typeName !== 'pdf' && loading" />
    <div
      class="image"
      v-if="
        typeName.startsWith('JPG') ||
        typeName.startsWith('jpg') ||
        typeName.startsWith('JPEG') ||
        typeName.startsWith('jpeg') ||
        typeName.startsWith('PNG') ||
        typeName.startsWith('png')
      "
    >
      <div>
        <img
          style="display: block; max-width: 100%; margin: 24px auto"
          :src="imgUrl"
          alt=""
        />
      </div>
    </div>
    <div class="docWrap" v-if="typeName == 'docx' || typeName == 'doc'">
      <!-- 预览文件的地方(用于渲染) -->
      <div ref="docFile" class="docPre"></div>
    </div>
    <div class="xlxs-pre" v-if="typeName == 'xlsx' || typeName == 'xls'">
      <div class="tab">
        <a-radio-group
          size="small"
          v-model:value="excel.sheetNameActive"
          @change="getSheetNameTable(excel.sheetNameActive)"
        >
          <a-radio-button
            v-for="(item, index) in excel.sheetNames"
            :key="index"
            :value="item"
            >{{ item }}</a-radio-button
          >
        </a-radio-group>
      </div>
      <div
        style="
          margin-top: 5px;
          border: 1px solid #a0a0a0;
          overflow-x: auto;
          overflow-y: scroll;
        "
      >
        <div v-html="excel.SheetActiveTable" style="padding: 10px 15px"></div>
      </div>
    </div>
    <div v-if="typeName == 'pdf'" style="height: 100%">
      <div id="pdf-preview" />
    </div>
    <div v-if="typeName == 'ppt' || typeName == 'pptx'" style="height: 100%">
      <iframe id="ppt-preview" width="100%" height="auto" :src="pptUrl" />
    </div>
    <div class="text-plain" v-if="typeName === 'txt'">
      <textarea readonly :value="txtText"></textarea>
    </div>
  </div>
</template>

<style lang="less" scoped>
.viewItemFile {
  height: 100%;
  .image {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;

    div {
      height: 600px;
      width: 600px;
    }
  }

  .divContent {
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

.viewItemFile {
  #pdf-preview .pinch-zoom-container {
    height: 100% !important;
  }

  .xlxs-pre {
    height: calc(100vh - 40px);
    padding: 20px;

    .table-html-wrap :deep(table) {
      border-right: 1px solid #e8eaec;
      border-bottom: 1px solid #e8eaec;
      border-collapse: collapse;
      margin: auto;
    }

    .table-html-wrap :deep(table td) {
      border-left: 1px solid #e8eaec;
      border-top: 1px solid #e8eaec;
      white-space: wrap;
      text-align: left;
      min-width: 100px;
      padding: 4px;
    }

    table {
      border-top: 1px solid #ebeef5;
      border-left: 1px solid #ebeef5;
      width: 100%;
      overflow: auto;

      tr {
        height: 44px;
      }

      td {
        min-width: 200px;
        max-width: 400px;
        padding: 4px 8px;
        border-right: 1px solid #ebeef5;
        border-bottom: 1px solid #ebeef5;
      }
    }
  }

  :deep(table) {
    width: 100% !important;
    border-collapse: collapse !important;
    border-spacing: 0 !important;
    text-align: center !important;
    border: 0px !important;
    overflow-x: auto !important;
    overflow-y: scroll !important;
  }

  :deep(table tr td) {
    /* border: 1px solid gray !important; */
    border-right: 1px solid gray !important;
    border-bottom: 1px solid gray !important;
    width: 300px !important;
    height: 33px !important;
  }

  /**整体样式 */
  :deep(.excel-view-container) {
    background-color: #ffffff;
  }

  /**标题样式 */
  :deep(.class4Title) {
    font-size: 22px !important;
    font-weight: bold !important;
    padding: 10px !important;
  }

  /**表格表头样式 */
  :deep(.class4TableTh) {
    /* font-size: 14px !important; */
    font-weight: bold !important;
    padding: 2px !important;
    background-color: #ccc !important;
  }
}

.docWrap {
  width: 100%;

  :deep(.docx-wrapper) {
    background: #ffffff !important;

    :deep(.docx-wrapper section) {
      padding: 8pt 16pt;
      width: 100%;
    }

    :deep(.docx-wrapper > section.docx) {
      box-shadow: 0;
    }
  }
}

html body {
  width: 100%;
  height: 100vh;
  margin: 0;
}
.text-plain {
  width: 100%;
  height: 100%;
  padding-top: 30px;
  textarea {
    width: 100%;
    height: 100%;
    border: none;
  }
  .visible-content {
    height: 100%;
    border: none;
  }
}
</style>

<style scoped>
.docWrap {
  width: 100%;
}

:deep(.docWrap .docx-wrapper) {
  background: #ffffff !important;
  padding: 16px !important;
}

:deep(.docWrap .docx-wrapper > section.docx) {
  padding: 8pt 16pt !important;
  width: 100% !important;
  overflow: scroll !important;
}

:deep(#pdf-preview .pinch-zoom-container) {
  height: auto !important;
}
</style>

即可。

posted @ 2024-07-23 17:30  罗毅豪  阅读(455)  评论(0编辑  收藏  举报