ADD scFileImport scFileExport 文件导入导出组件
This commit is contained in:
parent
2c2f36cdd7
commit
e0ba5e81dc
|
@ -16,6 +16,13 @@ export default {
|
|||
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: {
|
||||
menu: {
|
||||
url: `${config.API_URL}/file/menu`,
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in New Issue