nuxt、vue实现PDF和视频文件的上传、下载、预览
上传
上传页面
<el-form-item :label="(form.ququ3 == 1 ? '参培' : form.ququ3 == 2 ? '授课' : '') + '证明材料'" prop="ququ6">
<PdfUpload v-model="form.ququ6" :fileType="['pdf', 'mp4', 'avi', 'ts']"></PdfUpload>
</el-form-item>
PdfUpload组件
自定义组件实现v-model,只需要定义props:value属性来接收父组件的v-model绑定的初始值,在子组件改变值时触发$emit(‘input’,值)
<template>
<div class="upload-file ">
<el-upload multiple :action="uploadFileUrl" :before-upload="handleBeforeUpload" :file-list="fileList" :limit="limit"
:on-error="handleUploadError" :on-exceed="handleExceed" :on-success="handleUploadSuccess" :show-file-list="false"
:headers="headers" class="upload-file-uploader" ref="upload">
<el-button size="small" type="primary">点击上传</el-button>
<!-- 上传提示 -->
<div class="el-upload__tip" slot="tip" v-if="showTip">
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
<template v-if="fileType"> 只能上传PDF文件和视频,格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
</div>
</el-upload>
<!-- 文件列表 -->
<div class="ml-100" style="margin-top: -20px;">
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
<li :key="file.url" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
<el-link :href="file.url" :underline="false" target="_blank">
<!-- <span class="el-icon-document"> {{ getFileName(file.name) }}</span> -->
<span class="el-icon-document"> {{ file.fileName }}</span>
</el-link>
<div class="ele-upload-list__item-content-action ml-60" style="min-width: 40px;">
<el-link :underline="false" @click="handleDelete(index)" type="primary" icon="el-icon-circle-close"></el-link>
</div>
</li>
</transition-group>
</div>
</div>
</template>
<script>
// ******自定义组件实现v-model,只需要定义props:value属性来接收父组件的v-model绑定的初始值,在子组件改变值时触发$emit('input',值)
export default {
name: "FileUpload",
props: {
// 文件值,fileList
value: [String, Object, Array],
// 数量限制
limit: {
type: Number,
},
// 大小限制(MB)
fileSize: {
type: Number,
// default: 500,
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => ['png', 'jpg', 'jpeg'],
},
// 是否显示文件大小和类型限制提示文字
isShowTip: {
type: Boolean,
default: true
},
// 图片序号
indexValue: {
type: Number,
default: null,
},
},
data() {
return {
number: 0,
uploadList: [],
uploadFileUrl: '/prod-api' + "/file/upload", // 上传的图片服务器地址
headers: {
Authorization: this.$store.state.token,
},
fileList: [],
};
},
watch: {
value: {
handler(val) {
if (val) {
console.log(666, val);
// 然后将数组转为对象数组
this.fileList = val
} else {
this.fileList = [];
return [];
}
},
deep: true,
immediate: true,
}
},
computed: {
// 是否显示提示
showTip() {
return this.isShowTip && (this.fileType || this.fileSize);
},
},
methods: {
// 上传前校检格式和大小
handleBeforeUpload(file) {
console.log('上传前-校验', file, file.name);
// 校检文件类型
if (this.fileType) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
}
const isTypeOk = this.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
if (!isTypeOk) {
this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
return false;
}
}
// 校检文件大小
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize;
if (!isLt) {
this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`);
return false;
}
}
this.$modal.loading("正在上传,请稍候...");
this.number++;
return true;
},
// 文件个数超出
handleExceed() {
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
},
// 上传失败
handleUploadError(err) {
this.$modal.msgError("上传失败,请重试");
this.$modal.closeLoading()
},
// 上传成功回调
handleUploadSuccess(res) {
// name为 文件
console.log('上传成功回调', res, this.indexValue);
this.uploadList.push({ fileName: res.data.url.split('-')[res.data.url.split('-').length - 1], url: res.data.url, });
if (this.uploadList.length === this.number) {
this.fileList = this.fileList.concat(this.uploadList);
console.log(2222, this.fileList)
this.uploadList = [];
this.number = 0;
// this.$emit("input", this.listToString(this.fileList), this.indexValue);
this.$emit("input", this.fileList, this.indexValue);
this.$modal.closeLoading();
}
},
// 删除文件
handleDelete(index) {
this.fileList.splice(index, 1);
this.$emit("input", this.fileList);
},
// 获取文件名称
getFileName(name) {
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1);
} else {
return "";
}
},
// 对象转成指定字符串分隔
listToString(list, separator) {
let strs = "";
separator = separator || ",";
for (let i in list) {
strs += list[i].url + separator;
}
return strs != '' ? strs.substr(0, strs.length - 1) : '';
}
}
};
</script>
<style scoped >
.upload-file-uploader {
margin-bottom: 5px;
}
::v-deep .el-upload--picture-card {
width: 104px;
height: 104px;
line-height: 104px;
}
.upload-file-list {
max-height: 240px !important;
overflow-y: auto;
}
.upload-file-list .el-upload-list__item {
line-height: 2;
margin-bottom: 10px;
position: relative;
}
.upload-file-list .ele-upload-list__item-content {
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
}
.ele-upload-list__item-content-action .el-link {
margin-right: 10px;
}
</style>
整个上传页面
<template>
<div id="index" class="flex flex-top w ptb-30">
<Menu></Menu>
<div class="flex-1 ml-30 over-hidden">
<h3 class="mt-20 mb-40">新增培训</h3>
<el-form style="width:500px" :model="form" :rules="rules" ref="form" label-width="100px" class="demo-form">
<el-form-item label="培训主题名" prop="ququ1">
<el-input v-model.trim="form.ququ1" placeholder="请输入培训主题名" clearable />
</el-form-item>
<el-form-item label="培训分类" prop="ququ2">
<el-select v-model="form.ququ2" placeholder="请选择培训分类" style="width: 100%;">
<el-option label="5" value="5"></el-option>
</el-select>
</el-form-item>
<el-form-item label="所属类别" prop="ququ3">
<el-radio-group v-model="form.ququ3">
<el-radio label="1">参培人</el-radio>
<el-radio label="2">授课人</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="参与地点" prop="ququ4">
<el-input v-model.trim="form.ququ4" placeholder="请输入参与地点" clearable />
</el-form-item>
<el-form-item label="起止日期" prop="ququ5">
<el-date-picker v-model="form.ququ5" type="daterange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期" value-format="yyyy-MM-dd" style="width: 100%;">
</el-date-picker>
</el-form-item>
<el-form-item :label="(form.ququ3 == 1 ? '参培' : form.ququ3 == 2 ? '授课' : '') + '证明材料'" prop="ququ6">
<PdfUpload v-model="form.ququ6" :fileType="['pdf', 'mp4', 'avi', 'ts']"></PdfUpload>
</el-form-item>
<el-form-item label="备注" prop="ququ7">
<el-input v-model.trim="form.ququ7" placeholder="请输入备注" clearable type="textarea" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submit('form')">提交审核</el-button>
<el-button type="primary" plain @click="save">保存草稿</el-button>
<el-button @click="back">返回</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
form: {
ququ1: '',
ququ2: '',
ququ3: '',
ququ4: '',
ququ5: '',
ququ6: [],
ququ7: '',
},
rules: {
ququ1: {
required: true,
message: '请输入主题名',
trigger: ['blur']
},
ququ2: {
required: true,
message: '请选择培训分类',
trigger: ['change']
},
ququ3: {
required: true,
message: '请选择所属类别',
trigger: ['change']
},
ququ5: {
required: true,
message: '请选择起止日期',
trigger: ['change']
},
ququ6: {
required: true,
message: '请上传证明材料',
trigger: ['change']
},
}
};
},
mounted() {
if (localStorage.getItem('form')) {
this.$confirm('检测到您有保存的草稿,需要使用吗?', '提示', {
confirmButtonText: '使用',
cancelButtonText: '删除',
type: 'warning'
}).then(() => {
this.form = JSON.parse(localStorage.getItem('form'))
}).catch(() => {
localStorage.removeItem('form')
});
}
},
methods: {
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$axios.get('/exam/info/queryErrorQuestionQuantity').then(res => {
this.back()
localStorage.removeItem('form')
})
} else {
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
},
back() {
this.$router.back()
},
save() {
console.log(this.form.ququ6.map(item => item.url))
localStorage.setItem('form', JSON.stringify(this.form))
},
},
};
</script>
<style scoped></style>
预览和下载
预览页面
<el-button @click="downLoad(item)" type="primary" size="mini">下载</el-button>
<el-button @click="preview(item)" type="danger" size="mini">预览</el-button>
downLoad(item) {
let url = "https://cscec83-learnplatform.oss-cn-shanghai.aliyuncs.com/test/2023-11-27/6a12b9ca-3ae9-435b-b023-0f9c119afad3-1.mp4"
let url1 = "https://cscec83-learnplatform.oss-cn-shanghai.aliyuncs.com/test/2023-11-27/53aa1f60-0735-4203-86b2-c2b8b019e8d9-我的哈哈.pdf"
fetch(url).then(res => res.blob()).then(blob => { // 将链接地址字符内容转变成blob地址
const a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = url.split('-')[url.split('-').length - 1] // 下载文件的名字
document.body.appendChild(a)
a.click()
})
},
preview(item) {
let url = "https://cscec83-learnplatform.oss-cn-shanghai.aliyuncs.com/test/2023-11-27/6a12b9ca-3ae9-435b-b023-0f9c119afad3-1.mp4"
let url1 = "https://cscec83-learnplatform.oss-cn-shanghai.aliyuncs.com/test/2023-11-27/53aa1f60-0735-4203-86b2-c2b8b019e8d9-我的哈哈.pdf"
window.open(url1)
}
整个列表加详情弹窗页面
<template>
<div id="index" class="flex flex-top w ptb-30">
<Menu></Menu>
<div class="flex-1 ml-30 over-hidden">
<!-- 搜索 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
<el-form-item label="培训名称" prop="ququ1">
<el-input v-model.trim="queryParams.ququ1" placeholder="请输入培训名称" clearable @keyup.enter.native="getList(1)" />
</el-form-item>
<el-form-item label="起止日期" prop="ququ2">
<el-date-picker v-model="queryParams.ququ2" type="daterange" range-separator="至" start-placeholder="开始日期"
end-placeholder="结束日期" value-format="yyyy-MM-dd">
</el-date-picker>
</el-form-item>
<el-form-item label="培训地点" prop="ququ3">
<el-input v-model.trim="queryParams.ququ3" placeholder="请输入培训地点" clearable @keyup.enter.native="getList(1)" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="getList(1)">查询</el-button>
<el-button size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 新增 -->
<el-row>
<el-col :span="24" class="flex_r">
<el-button type="success" icon="el-icon-plus" size="mini" @click="add">新增</el-button>
</el-col>
</el-row>
<!-- tab -->
<el-tabs v-model="tab" @tab-click="getList(1)">
<el-tab-pane label="全部培训" name="1">
</el-tab-pane>
<el-tab-pane label="已通过审批" name="2">
</el-tab-pane>
<el-tab-pane label="未通过审批" name="3">
</el-tab-pane>
</el-tabs>
<!-- 表格 -->
<el-table :data="tableData" border>
<el-table-column prop="paperName" align="center" min-width="140" label="培训名称" :show-overflow-tooltip="true">
</el-table-column>
<el-table-column prop="paperName" align="center" min-width="140" label="培训分类" :show-overflow-tooltip="true">
</el-table-column>
<el-table-column prop="practiceType" align="center" min-width="90" label="所属类别">
<template slot-scope="scope">
<el-tag size="small">
{{ scope.row.practiceType == 1 ? '参培人' : '授课人' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="paperName" align="center" min-width="100" label="培训地点" :show-overflow-tooltip="true">
</el-table-column>
<el-table-column prop="startTime" align="center" min-width="120" label="开始时间">
</el-table-column>
<el-table-column prop="endTime" align="center" min-width="120" label="结束时间">
</el-table-column>
<el-table-column prop="practiceType" align="center" min-width="90" label="审批状态">
<template slot-scope="scope">
<el-tag :type="scope.row.practiceType == 1 ? '' : scope.row.practiceType == 2 ? 'success' : 'danger'">
{{ scope.row.practiceType == 1 ? '审核中' : scope.row.practiceType == 2 ? '已通过' : '未通过' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" min-width="100">
<template slot-scope="scope">
<el-button @click="toDetail(scope.row)" size="mini" type="text" icon="el-icon-document-copy">详情</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="center mtb-20">
<el-pagination :hide-on-single-page="true" background layout="prev, pager, next" :total="total * 1"
:current-page.sync="page" @current-change="getList" :page-size="this.pageSize">
</el-pagination>
</div>
</div>
<!-- 详情弹窗 -->
<el-dialog title="培训详情" :visible.sync="show" width="1000px">
<el-card shadow="hover" class="flex-1 ml-20 scroll pd-10">
<el-descriptions title="培训详情">
<el-descriptions-item label="培训主题名">kooriookami</el-descriptions-item>
<el-descriptions-item label="培训分类">18100000000</el-descriptions-item>
<el-descriptions-item label="提交时间">苏州市</el-descriptions-item>
<el-descriptions-item label="本人所属类别">
<el-tag size="small">学校</el-tag>
</el-descriptions-item>
<el-descriptions-item label="培训起止日期">江苏省苏州市吴中区吴中大道 1188 号</el-descriptions-item>
<el-descriptions-item label="参与地点">江苏省苏州市吴中区吴中大道 1188 号</el-descriptions-item>
<el-descriptions-item label="备注">江苏省苏州市吴中区吴中大道 1188 号</el-descriptions-item>
</el-descriptions>
</el-card>
<el-card shadow="hover" class="flex-1 ml-20 scroll pd-10 mt-30">
<h3 class="mb-20">附件记录</h3>
<div class="flex mb-10" v-for="(item, i) in 6">
<div>23243232.png</div>
<div>
<el-button @click="downLoad(item)" type="primary" size="mini">下载</el-button>
<el-button @click="preview(item)" type="danger" size="mini">预览</el-button>
</div>
</div>
</el-card>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
return {
tab: '1',
page: 1,
pageSize: 10,
total: 0,
tableData: [],
// 搜索
queryParams: {
ququ1: "",
ququ2: "",
ququ3: "",
},
//详情
show: false,
};
},
mounted() {
this.getList()
},
methods: {
getList(page) {
console.log(this.queryParams.ququ2)
if (page == 1) {
this.page = 1;
}
this.$axios.get('/exam/practiceHistory/record/listPc', {
params: {
pageNum: this.page,
pageSize: this.pageSize,
tab: this.tab,
...this.queryParams
}
}).then(res => {
this.tableData = res.rows;
this.total = res.total;
});
},
/** 重置按钮操作 */
resetQuery() {
this.$refs['queryForm'].resetFields();
this.queryParams.ququ2 = ''
this.getList(1)
},
add() {
this.$router.push({ path: "/train/add" });
},
toDetail(row) {
this.show = true
},
downLoad(item) {
let url = "https://cscec83-learnplatform.oss-cn-shanghai.aliyuncs.com/test/2023-11-27/6a12b9ca-3ae9-435b-b023-0f9c119afad3-1.mp4"
let url1 = "https://cscec83-learnplatform.oss-cn-shanghai.aliyuncs.com/test/2023-11-27/53aa1f60-0735-4203-86b2-c2b8b019e8d9-我的哈哈.pdf"
fetch(url).then(res => res.blob()).then(blob => { // 将链接地址字符内容转变成blob地址
const a = document.createElement('a')
a.href = URL.createObjectURL(blob)
a.download = url.split('-')[url.split('-').length - 1] // 下载文件的名字
document.body.appendChild(a)
a.click()
})
},
preview(item) {
let url = "https://cscec83-learnplatform.oss-cn-shanghai.aliyuncs.com/test/2023-11-27/6a12b9ca-3ae9-435b-b023-0f9c119afad3-1.mp4"
let url1 = "https://cscec83-learnplatform.oss-cn-shanghai.aliyuncs.com/test/2023-11-27/53aa1f60-0735-4203-86b2-c2b8b019e8d9-我的哈哈.pdf"
window.open(url1)
}
},
};
</script>
<style scoped></style>