ADD scFileImport scFileExport 文件导入导出组件

This commit is contained in:
sakuya 2022-05-25 13:16:50 +08:00
parent 2c2f36cdd7
commit e0ba5e81dc
5 changed files with 506 additions and 0 deletions

View File

@ -16,6 +16,13 @@ export default {
return await http.post(this.url, data, config); return await http.post(this.url, data, config);
} }
}, },
exportFile: {
url: `${config.API_URL}/fileExport`,
name: "导出附件",
get: async function(data, config={}){
return await http.get(this.url, data, config);
}
},
file: { file: {
menu: { menu: {
url: `${config.API_URL}/file/menu`, url: `${config.API_URL}/file/menu`,

View File

@ -0,0 +1,53 @@
<template>
<el-table ref="table" :data="columnData" row-key="prop" style="width: 100%" border>
<el-table-column prop="" label="排序" width="58">
<el-tag class="move" style="cursor: move;"><el-icon style="cursor: move;"><el-icon-d-caret/></el-icon></el-tag>
</el-table-column>
<el-table-column prop="label" label="列名">
<template #default="scope">
<el-tag round :effect="scope.row.hide?'light':'dark'" :type="scope.row.hide?'info':''">{{ scope.row.label }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="hide" label="显示" width="60">
<template #default="scope">
<el-switch v-model="scope.row.hide" size="small" :active-value="false" :inactive-value="true"/>
</template>
</el-table-column>
</el-table>
</template>
<script>
import Sortable from 'sortablejs'
export default {
emits: ['success'],
props: {
column: { type: Array, default: () => [] }
},
data() {
return {
columnData: this.column
}
},
mounted() {
this.rowDrop()
},
methods: {
rowDrop(){
const _this = this
const tbody = this.$refs.table.$el.querySelector('.el-table__body-wrapper tbody')
Sortable.create(tbody, {
handle: ".move",
animation: 200,
ghostClass: "ghost",
onEnd({ newIndex, oldIndex }) {
const tableData = _this.columnData
const currRow = tableData.splice(oldIndex, 1)[0]
tableData.splice(newIndex, 0, currRow)
}
})
}
}
}
</script>

View File

@ -0,0 +1,168 @@
<!--
* @Descripttion: 文件导出
* @version: 1.0
* @Author: sakuya
* @Date: 2022年5月24日16:20:12
* @LastEditors:
* @LastEditTime:
-->
<template>
<slot :open="open">
<el-button type="primary" plain @click="open">导出</el-button>
</slot>
<el-drawer v-model="dialog" title="导出" :size="400" direction="rtl" append-to-body destroy-on-close>
<el-main style="padding: 0 20px 20px 20px;">
<div v-loading="downLoading" element-loading-text="正在处理中...">
<div v-if="downLoading && progress" style="position: absolute;width: 100%;height: 100%;display: flex;justify-content: center;align-items: center;z-index: 3000;">
<el-progress :text-inside="true" :stroke-width="20" :percentage="downLoadProgress" style="width: 100%;margin-bottom: 120px;"/>
</div>
<el-tabs>
<el-tab-pane label="常规" lazy>
<el-form label-width="100px" label-position="left" style="margin: 10px 0 20px 0;">
<el-form-item label="文件名">
<el-input v-model="formData.fileName" placeholder="请输入文件名" />
</el-form-item>
<el-form-item label="文件类型">
<el-select v-model="formData.fileType" placeholder="请选择文件类型">
<el-option v-for="item in fileTypes" :key="item" :label="'*.'+item" :value="item" />
</el-select>
</el-form-item>
<slot name="form" :formData="formData"></slot>
</el-form>
<el-button type="primary" size="large" icon="el-icon-download" style="width: 100%;" @click="download"> </el-button>
</el-tab-pane>
<el-tab-pane label="列设置" v-if="columnData.length>0" lazy>
<columnSet :column="columnData"></columnSet>
</el-tab-pane>
<el-tab-pane label="其他参数" v-if="data" lazy>
<el-descriptions :column="1" border size="small">
<el-descriptions-item v-for=" (val, key) in data" :key="key" :label="key">{{val}}</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
</el-tabs>
</div>
</el-main>
</el-drawer>
</template>
<script>
import columnSet from './column'
export default {
components: {
columnSet
},
props: {
apiObj: { type: Object, default: () => {} },
fileName: { type: String, default: "" },
fileTypes: { type: Array, default: () => ['xlsx'] },
data: { type: Object, default: () => {} },
column: { type: Array, default: () => [] },
blob: { type: Boolean, default: false },
progress: { type: Boolean, default: true }
},
data() {
return {
dialog: false,
formData: {
fileName: this.fileName,
fileType: this.fileTypes[0]
},
columnData: [],
downLoading: false,
downLoadProgress: 0
}
},
watch:{
'formData.fileType'(val) {
if(this.formData.fileName.includes(".")){
this.formData.fileName = this.formData.fileName.substring(0, this.formData.fileName.lastIndexOf('.')) + "." + val
}else{
this.formData.fileName = this.formData.fileName + "." + val
}
}
},
mounted() {
},
methods: {
open() {
this.dialog = true
this.formData = {
fileName: (this.fileName?this.fileName:(new Date().getTime()+'')) + "." + this.fileTypes[0],
fileType: this.fileTypes[0]
}
this.columnData = JSON.parse(JSON.stringify(this.column))
},
close() {
this.dialog = false
},
download() {
let columnArr = {
column: this.columnData.filter(n => !n.hide).map(n => n.prop).join(",")
}
let assignData = {...this.data, ...this.formData, ...columnArr}
if(this.blob){
this.downloadFile(this.apiObj, this.formData.fileName, assignData)
}else{
this.linkFile(this.apiObj.url, this.formData.fileName, assignData)
}
},
linkFile(url, fileName, data={}){
let a = document.createElement("a")
a.style = "display: none"
a.target = "_blank"
//a.download = fileName
a.href = url + this.toQueryString(data)
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
},
downloadFile(apiObj, fileName, data={}){
this.downLoading = true
var _this = this
apiObj.get(data, {
responseType: 'blob',
onDownloadProgress(e){
if(e.lengthComputable){
_this.downLoadProgress = parseInt(e.loaded / e.total * 100)
}
}
}).then(res => {
this.downLoading = false
this.downLoadProgress = 0
let url = URL.createObjectURL(res)
let a = document.createElement("a")
a.style = "display: none"
a.target = "_blank"
a.download = fileName
a.href = url
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}).catch(err => {
this.downLoading = false
this.downLoadProgress = 0
this.$notify.error({
title: '下载文件失败',
message: err
})
})
},
toQueryString(obj){
let arr = []
for (var k in obj) {
arr.push(`${k}=${obj[k]}`)
}
return (arr.length>0?"?":"") + arr.join('&')
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,133 @@
<!--
* @Descripttion: 文件导入
* @version: 1.0
* @Author: sakuya
* @Date: 2022年5月24日11:30:03
* @LastEditors:
* @LastEditTime:
-->
<template>
<slot :open="open">
<el-button type="primary" plain @click="open">导入</el-button>
</slot>
<el-dialog v-model="dialog" title="导入" :width="550" :close-on-click-modal="false" append-to-body destroy-on-close>
<el-progress v-if="loading" :text-inside="true" :stroke-width="20" :percentage="percentage" style="margin-bottom: 15px;"/>
<div v-loading="loading">
<el-upload ref="uploader"
drag
:accept="accept"
:maxSize="maxSize"
:limit="1"
:data="data"
:show-file-list="false"
:http-request="request"
:before-upload="before"
:on-progress="progress"
:on-success="success"
:on-error="error"
>
<slot name="uploader">
<el-icon class="el-icon--upload"><el-icon-upload-filled /></el-icon>
<div class="el-upload__text">
将文件拖到此处或 <em>点击选择文件上传</em>
</div>
</slot>
<template #tip>
<div class="el-upload__tip">
<template v-if="tip">{{tip}}</template>
<template v-else>请上传小于或等于 {{maxSize}}M {{accept}} 格式文件</template>
<p v-if="templateUrl" style="margin-top: 7px;">
<el-link :href="templateUrl" target="_blank" type="primary" :underline="false">下载导入模板</el-link>
</p>
</div>
</template>
</el-upload>
<el-form v-if="$slots.form" inline label-width="100px" label-position="left" style="margin-top: 18px;">
<slot name="form" :formData="formData"></slot>
</el-form>
</div>
</el-dialog>
</template>
<script>
export default {
emits: ['success'],
props: {
apiObj: { type: Object, default: () => {} },
data: { type: Object, default: () => {} },
accept: { type: String, default: ".xls, .xlsx" },
maxSize: { type: Number, default: 10 },
tip: { type: String, default: "" },
templateUrl: { type: String, default: "" }
},
data() {
return {
dialog: false,
loading: false,
percentage: 0,
formData: {}
}
},
mounted() {
},
methods: {
open(){
this.dialog = true
this.formData = {}
},
close(){
this.dialog = false
},
before(file){
const maxSize = file.size / 1024 / 1024 < this.maxSize;
if (!maxSize) {
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
return false;
}
this.loading = true
},
progress(e){
this.percentage = e.percent
},
success(res, file){
this.$refs.uploader.handleRemove(file)
this.$refs.uploader.clearFiles()
this.loading = false
this.percentage = 0
this.$emit('success', res, this.close)
},
error(err){
this.loading = false
this.percentage = 0
this.$notify.error({
title: '上传文件未成功',
message: err
})
},
request(param){
Object.assign(param.data, this.formData)
const data = new FormData();
data.append(param.filename, param.file);
for (const key in param.data) {
data.append(key, param.data[key]);
}
this.apiObj.post(data, {
onUploadProgress: e => {
const complete = parseInt(((e.loaded / e.total) * 100) | 0, 10)
param.onProgress({percent: complete})
}
}).then(res => {
param.onSuccess(res)
}).catch(err => {
param.onError(err)
})
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,145 @@
<template>
<el-main>
<el-row :gutter="15">
<el-col :lg="12">
<el-card shadow="never" header="导入">
<sc-file-import :apiObj="$API.common.uploadFile" templateUrl="http://www.scuiadmin/file.xlsx" @success="success"></sc-file-import>
<sc-file-import :apiObj="$API.common.uploadFile" :data="{otherData:'demo'}" templateUrl="http://www.scuiadmin/file.xlsx" accept=".xls, .xlsx" :maxSize="30" tip="请上传小于或等于 30M 的 .xls, .xlsx 格式文件(自定义TIP)" @success="success">
<template #default="{open}">
<el-button type="primary" icon="sc-icon-upload" @click="open">导入(全配置)</el-button>
</template>
<template #uploader>
<el-icon class="el-icon--upload"><sc-icon-file-excel /></el-icon>
<div class="el-upload__text">
将文件拖到此处或 <em>点击选择文件上传</em>
</div>
</template>
<template #form="{formData}">
<el-form-item label="覆盖已有数据">
<el-switch v-model="formData.coverage" />
</el-form-item>
<el-form-item label="跳过错误数据">
<el-switch v-model="formData.skipError" />
</el-form-item>
</template>
</sc-file-import>
<el-descriptions :column="1" border size="small" style="margin-top: 15px;">
<el-descriptions-item label="apiObj" :width="200">Object 文件上传接口对象</el-descriptions-item>
<el-descriptions-item label="data">Object 上传时附带的额外参数</el-descriptions-item>
<el-descriptions-item label="accept">String 可选择文件类型默认为".xls, .xlsx"</el-descriptions-item>
<el-descriptions-item label="maxSize">Number 可选择文件大小单位为M默认为10</el-descriptions-item>
<el-descriptions-item label="tip">String 上传框底下的提示语句默认为"请上传小于或等于 {maxSize}M 的 {accept} 格式文件"</el-descriptions-item>
<el-descriptions-item label="templateUrl">String 模板的下载URL</el-descriptions-item>
<el-descriptions-item label="@success">事件 上传接口返回的事件返回function(res, close)执行close()将关闭窗口</el-descriptions-item>
<el-descriptions-item label='#default="{open}"'>插糟 默认触发按钮插糟返回open()打开窗口函数可以绑定元素@click事件</el-descriptions-item>
<el-descriptions-item label='#uploader'>插糟 自定义上传框插槽</el-descriptions-item>
<el-descriptions-item label='#form="{formData}"'>插糟 自定义表单组件插槽formData都将作为上传时附带的额外参数</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
<el-col :lg="12">
<el-card shadow="never" header="导出">
<sc-file-export :apiObj="$API.common.exportFile"></sc-file-export>
<sc-file-export :apiObj="$API.common.exportFile" blob fileName="人员列表" :data="{otherData:'demo'}" :column="column" :fileTypes="['xlsx','docx','pdf']">
<template #default="{open}">
<el-button type="primary" icon="sc-icon-download" @click="open">导出(blob文件流)</el-button>
</template>
<template #form="{formData}">
<el-form-item label="导出条数">
<el-select v-model="formData.limit" placeholder="Select">
<el-option label="100条" value="100" />
<el-option label="500条" value="500" />
<el-option label="1000条" value="1000" />
<el-option label="5000条" value="5000" />
<el-option label="10000条" value="10000" />
</el-select>
</el-form-item>
</template>
</sc-file-export>
<el-descriptions :column="1" border size="small" style="margin-top: 15px;">
<el-descriptions-item label="apiObj" :width="200">Object 文件导出接口对象通过apiObj.url请求文件</el-descriptions-item>
<el-descriptions-item label="data">Object 上传时附带的额外参数(可为数据表格的过滤项)</el-descriptions-item>
<el-descriptions-item label="fileName">String 下载文件名称默认为当前时间戳</el-descriptions-item>
<el-descriptions-item label="fileTypes">Array 可选择文件类型默认为['xlsx']组件将数组第一项当做已选项</el-descriptions-item>
<el-descriptions-item label="column">Array 列配置请求文件时将添加column为key的参数值为prop逗号","分割的字符串</el-descriptions-item>
<el-descriptions-item label="blob">Boolean 是否由游览器请求文件返回blob后提供下载</el-descriptions-item>
<el-descriptions-item label="progress">Boolean blob开启后是否显示下载文件进度条当服务器启用Gzip时建议关闭因为获取到的文件总数和下载总数不匹配</el-descriptions-item>
<el-descriptions-item label='#default="{open}"'>插糟 默认触发按钮插糟返回open()打开窗口函数可以绑定元素@click事件</el-descriptions-item>
<el-descriptions-item label='#form="{formData}"'>插糟 自定义表单组件插槽formData都将作为请求时附带的额外参数</el-descriptions-item>
</el-descriptions>
</el-card>
</el-col>
</el-row>
</el-main>
</template>
<script>
import scFileImport from '@/components/scFileImport'
import scFileExport from '@/components/scFileExport'
export default {
name: 'importexport',
components: {
scFileImport,
scFileExport
},
data() {
return {
column: [
{
label: "姓名",
prop: "name"
},
{
label: "性别",
prop: "sex"
},
{
label: "评分",
prop: "num"
},
{
label: "邮箱",
prop: "email",
hide: true
},
{
label: "进度",
prop: "progress"
},
{
label: "注册时间",
prop: "datetime"
}
]
}
},
mounted() {
},
methods: {
success(res, close){
if(res.code==200){
this.$alert(res, "导入成功", {
type: "success",
showClose: false,
center: true
})
close()
}else{
this.$alert(res, "导入失败", {
type: "error",
showClose: false,
center: true
})
}
}
}
}
</script>
<style>
</style>