需求:JS实现文件预览功能,可预览图片、Word文档、Excel表格、PDF文件、TXT文件。
Word预览:
需要安装docx-preview
Excel预览:
需要安装xlsx
PDF预览:
需要安装pdfh5
如果报错
则需要安装canvas,因为pdfh5依赖canvas
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;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
预览相关的全部代码示例如下:
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>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
- 331.
- 332.
- 333.
- 334.
- 335.
- 336.
- 337.
- 338.
- 339.
- 340.
- 341.
- 342.
- 343.
- 344.
- 345.
- 346.
- 347.
- 348.
- 349.
- 350.
- 351.
- 352.
- 353.
- 354.
- 355.
- 356.
- 357.
- 358.
- 359.
- 360.
- 361.
- 362.
- 363.
- 364.
- 365.
- 366.
- 367.
- 368.
- 369.
- 370.
- 371.
- 372.
- 373.
- 374.
- 375.
- 376.
- 377.
- 378.
- 379.
- 380.
- 381.
- 382.
- 383.
- 384.
- 385.
- 386.
- 387.
- 388.
- 389.
- 390.
- 391.
- 392.
- 393.
- 394.
- 395.
- 396.
- 397.
- 398.
- 399.
- 400.
- 401.
- 402.
- 403.
- 404.
- 405.
- 406.
- 407.
- 408.
- 409.
- 410.
- 411.
- 412.
- 413.
- 414.
- 415.
- 416.
- 417.
- 418.
- 419.
- 420.
- 421.
- 422.
- 423.
- 424.
- 425.
- 426.
- 427.
- 428.
- 429.
- 430.
- 431.
- 432.
- 433.
- 434.
- 435.
- 436.
- 437.
- 438.
- 439.
- 440.
- 441.
- 442.
- 443.
- 444.
- 445.
- 446.
- 447.
- 448.
- 449.
- 450.
- 451.
- 452.
- 453.
- 454.
- 455.
- 456.
- 457.
- 458.
- 459.
- 460.
- 461.
- 462.
- 463.
- 464.
- 465.
- 466.
- 467.
- 468.
- 469.
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>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.
- 304.
- 305.
- 306.
- 307.
- 308.
- 309.
- 310.
- 311.
- 312.
- 313.
- 314.
- 315.
- 316.
- 317.
- 318.
- 319.
- 320.
- 321.
- 322.
- 323.
- 324.
- 325.
- 326.
- 327.
- 328.
- 329.
- 330.
- 331.
- 332.
- 333.
- 334.
- 335.
- 336.
- 337.
- 338.
- 339.
- 340.
- 341.
- 342.
- 343.
- 344.
- 345.
- 346.
- 347.
- 348.
- 349.
- 350.
- 351.
- 352.
- 353.
- 354.
- 355.
- 356.
- 357.
- 358.
- 359.
- 360.
- 361.
- 362.
- 363.
- 364.
- 365.
- 366.
- 367.
- 368.
- 369.
- 370.
- 371.
- 372.
- 373.
- 374.
- 375.
- 376.
- 377.
- 378.
- 379.
- 380.
- 381.
- 382.
- 383.
- 384.
- 385.
- 386.
- 387.
- 388.
- 389.
- 390.
- 391.
- 392.
- 393.
- 394.
- 395.
- 396.
- 397.
- 398.
- 399.
- 400.
- 401.
- 402.
- 403.
- 404.
- 405.
- 406.
- 407.
- 408.
- 409.
- 410.
- 411.
- 412.
- 413.
- 414.
- 415.
- 416.
- 417.
- 418.
- 419.
- 420.
- 421.
- 422.
- 423.
- 424.
- 425.
- 426.
- 427.
- 428.
- 429.
- 430.
- 431.
- 432.
- 433.
- 434.
- 435.
- 436.
- 437.
- 438.
- 439.
- 440.
- 441.
- 442.
- 443.
- 444.
- 445.
- 446.
- 447.
- 448.
- 449.
- 450.
- 451.
- 452.
- 453.
- 454.
- 455.
- 456.
- 457.
- 458.
- 459.
- 460.
- 461.
- 462.
- 463.
- 464.
- 465.
- 466.
- 467.
- 468.
- 469.
- 470.
- 471.
- 472.
- 473.
- 474.
- 475.
- 476.
- 477.
- 478.
- 479.
- 480.
- 481.
- 482.
- 483.
- 484.
- 485.
- 486.
- 487.
即可。
所有评论(0)