本文是用來記錄項目中遇到各類業務場景下的導出功能實現。歡迎點贊收藏html
這種導出,我最喜歡了,後端比較有良心,前端很省事。固然在這種場景下,還分兩種狀況。前端
if (res.code == 200) {
window.location.href = res.data;
}
複製代碼
連接不帶域名vue
域名是請求後端的域名,不是前端頁面的域名。項目通常是給多個客戶使用,故後端的域名通常都交給運維來配置。項目中這麼實現。ios
在靜態資源public文件夾中創建config.js文件和config.js.example文件,其中config.js文件是忽悠上傳到git上的,開發環境能夠裏面配置後端的域名,在生產環境是由運維在裏面配置後端的域名,由於已經忽悠上傳到git上的,更新代碼時不會覆蓋運維所配置的。config.js.example文件是一個示例做用,告訴運維怎麼配置。git
config.js和config.js.example內容同樣github
/*
配置文件示列:
配置文件路徑 public/config.js
*/
window.apiConfig = {
baseUrl: '後端的域名',
};
複製代碼
而後再public/index.html引入config.jsweb
<script>
var script = document.createElement('script');
var num = Math.floor(Math.random() * 10000);
script.src = 'config.js?a=' + num;
document.getElementsByTagName('head')[0].appendChild(script);
script = document.getElementById('scriptConfig');
script.parentNode.removeChild(script);
script = null;
</script>
複製代碼
最後這麼使用便可npm
if (res.code == 200) {
window.location.href = window.apiConfig.baseUrl+res.data;
}
複製代碼
export function export(data){
return service.get('接口地址',{
params:data,
responseType:'blob'
})
}
複製代碼
new Blob()
來處理二進制數據,生成一文件,再用createObjectURL()
建立連接後,用a連接自動下載。下面把方法封裝一下,掛在Vue原型鏈上。const install = function(Vue,opts){
* 處理二進制數據導出
* @param blob 二進制流
* @param name 文件名
*/
Vue.prototype.exportExcels = function(blob,name){
// type 爲須要導出的文件類型,此處爲xls表格類型
const file = new Blob([blob], { type: 'application/vnd.ms-excel' });
// 兼容不一樣瀏覽器的URL對象
const url = window.URL || window.webkitURL || window.moxURL;
// 建立下載連接
const downloadHref = url.createObjectURL(file);
// 建立a標籤併爲其添加屬性
let downloadLink = document.createElement('a');
downloadLink.setAttribute('href', downloadHref);
downloadLink.setAttribute('download', name);
//將a標籤添加到body中
document.body.appendChild(downloadLink);
// 觸發a標籤的點擊,自動下載
downloadLink.click();
//下載完成後移除a標籤
document.body.removeChild(downloadLink);
//釋放下載連接
url.revokeObjectURL(downloadHref);
}
}
export default{
install
}
複製代碼
其中new Blob()
的第一個參數是array,裏面每項是一個二進制流,第二個參數是可選屬性,其中type
屬性是文件的MIME類型,這個類型由後端決定是什麼類型。json
經常使用的MIME類型以下canvas
後綴名 | MIME名稱 |
---|---|
*.csv | text/csv |
*.doc | application/msword |
*.dot | application/msword |
*.xls | application/vnd.ms-excel |
*.xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
上面兩種場景都是先要請求服務器,獲得返回數據後處理後再下載。這種場景是在導出接口地址上加參數直接打開下載,例如:
window.location.href = '導出接口地址'?user='lhy'&date='2020-05';
複製代碼
上面的請求方法至關get方法,可是當請求導出接口時候參數太多了,使用get方法請求會致使參數缺乏。這時候就想辦法用post方法請求。
因爲這種場景是直接打開導出接口地址下載,有點很差用post方法,那麼這時候就要藉助HTML<form>
標籤和DOM Form 對象來解決。
咱們封裝一個組件來實現。
<template>
<form :action="action" :target="target" :method="method" ref="exports">
<template v-if="data.length">
<input type="hidden" autocomplete="off" v-for="(item,i) in data" :name="item.name" :value="item.value"/>
</template>
<input type="hidden" autocomplete="off" readonly name="token" :value="token"/>
</form>
</template>
<script>
export default {
name: 'formExport',
props: {
action: {
type: String,
default: '',
},
target: {
type: String,
default: '_blank',
},
method: {
type: String,
default: 'post',
},
token:{
type: String,
default: '',
},
data: {
type: Array,
default() {
return [];
}
}
},
methods: {
submit() {
return new Promise((resoleve,reject) =>{
if (this.token) {
this.$refs.exports.submit();
resolv()
}else{
reject()
}
}
}
}
}
</script>
複製代碼
組件文檔
參數 | 說明 | 類型 | 可選值 | 默認值 |
---|---|---|---|---|
action | 必填,導出接口地址 | String | — | — |
target | 規定在何處打開導出接口地址 | String | _blank:在新窗口打開 _self:在當前窗口打開 |
_blank |
method | 請求方法 | String | post/get | post |
token | 必填,鑑權 | String | — | — |
data | 必填,傳給服務器的參數 {name:參數名稱,value:參數值} |
String | — | — |
事件名稱 | 說明 | 回調參數 |
---|---|---|
submit | 提交表單 | Promise對象 |
<template>
<formExport ref="export" :action="exportData.url" :data="exportData.url" :token="exportData.token"></formExport>
<el-button @click="handleExport">導出</el-button>
</template>
<script>
export default {
data(){
return{
exportData:{
url:'導出接口地址',
data:{
user:'lds',
page:1,
pageSize:20,
statTime:'2020-04',
endTime:'2020-05'
},
token:'12334f'
}
}
},
components:{
formExport: () =>import('./formExport.vue')
},
methods:{
handleExport(){
setTimeout(() => {
this.$refs.export.submit();
}, 500);
}
}
}
</script>
複製代碼
這裏要藉助xlsx和file-saver兩個插件實現。其中xlsx是生成Excel文件,file-save是保存下載Excel文件
npm install xlsx --save
npm install file-saver --save
複製代碼
下面把方法封裝一下,掛在Vue原型鏈上
參數 | 說明 | 類型 | 默認 | 可選值 | 示例 |
---|---|---|---|---|---|
header | 表格頭數據 | array | — | — | ['一級客戶', '申請客戶', '支付寶帳號', '提現金額', '申請人', '申請時間'] |
data | 表格數據 | array | — | — | [{},{}] |
filename | Excel文件名稱 | string | — | — | 'excle1' |
opition | 額外配置 | object | — | — | {} |
參數 | 說明 | 類型 | 默認 | 可選值 | 示例 |
---|---|---|---|---|---|
filterVal | 過濾表格數據 | array | — | — | ['id','name'] |
multiHeader | 表格頭數據除最後一行表格頭的數據,是個二維數據,不夠的用''補全,只在bookType爲xlsx或xls下有效。 | array | — | — | [ ['序號', '客戶信息', '', '', '', ''], ['', '客戶姓名', '提現信息', '', '', '']] |
merges | 合併表格頭的規則,只在bookType爲xlsx或xls下有效。 | array | — | — | ['A1:A3', 'B1:F1', 'B2:B3', 'C2:F2'] |
autoWidth | 表格內容是否自適應寬度 | boolean | true | true/false | — |
bookType | 生成文件類型 | string | xlsx | xlsx/xls/csv | — |
const install = function(Vue,opts){
/**
* json數據生成Excel並下載
* @param header 表格頭數據
* @param data 表格數據
* @param filename Excel文件名稱
* @param opition 額外配置
*/
Vue.prototype.downloadExcels = function (header, data, filename, opition) {
let defaultOpition = {
filterVal: [],
multiHeader: [],
merges: [],
autoWidth: true,
bookType: 'xlsx',
}
if (header && Object.prototype.toString.call(header) != '[object Array]') {
throw new Error('header請傳入數組');
}
if (data && Object.prototype.toString.call(data) != '[object Array]') {
throw new Error('data請傳入數組');
}
if (opition && Object.prototype.toString.call(opition) == '[object Object]') {
defaultOpition = Object.assign({}, defaultOpition, opition);
}
if (Object.prototype.toString.call(defaultOpition.filterVal) != '[object Array]') {
throw new Error('filterVal請傳入數組');
}
if (Object.prototype.toString.call(defaultOpition.multiHeader) != '[object Array]') {
throw new Error('multiHeader請傳入數組');
}
if (Object.prototype.toString.call(defaultOpition.merges) != '[object Array]') {
throw new Error('merges請傳入數組');
}
const formatJson = function (filterVal, jsonData) {
if (filterVal.length == 0) {
return jsonData;
} else {
return jsonData.map(v => filterVal.map(j => v[j]));
}
}
data = formatJson(defaultOpition.filterVal, data);
if (data[0].length > header.length) {
throw new Error('data中每項數據長度大於頭部長度');
}
defaultOpition['data'] = data;
defaultOpition['header'] = header;
defaultOpition['filename'] = filename;
import('./Export2Excel').then(res => {
res.export_json_to_excel(defaultOpition);
})
}
}
export default{
install
}
複製代碼
handleExport() {
const header = ['一級客戶', '申請客戶', '支付寶帳號', '提現金額', '申請人', '申請時間'];
const option = {
bookType: 'xlsx',
filterVal: ['firstCustomName', 'custom_name', 'withdrawals_bank', 'withdrawals_amount', 'do_username', 'add_time'],
multiHeader: [
['序號', '客戶信息', '', '', '', ''],
['', '客戶姓名', '提現信息', '', '', '']
],
merges: ['A1:A3', 'B1:F1', 'B2:B3', 'C2:F2']
}
this.downloadExcels(header, this.tableData, '審覈記錄', option)
},
複製代碼
這種導出適用可視化數據場景
const install = function(Vue,opts){
* 將頁面導出成pdf文件
* @param id 要生成PDF文件DOM區域的id
* @param fileName 導出的文件名稱
* @param height 導出的pdf高度不夠,須要設置額外高度,默認80
*/
Vue.prototype.exportPDF= function(id, fileName, height = 80){
//html2canvas只截取dom的可視區域,將dom的可視區域設置大解決導出視圖不全的問題
document.getElementById(id).ownerDocument.defaultView.innerHeight = document.getElementById(id).scrollHeight + height;
html2canvas(document.getElementById(id), {
scale: 2,//按比例增長分辨率 (2=雙倍).
dpi: 1080,//導出pdf清晰度 將分辨率提升到特定的DPI(每英寸點數)
background: "#fff", //背景設爲白色(默認爲黑色)
onrendered: function (canvas) {
let contentWidth = canvas.width;
let contentHeight = canvas.height;
//一頁pdf顯示html頁面生成的canvas高度;
//a4紙的尺寸[595.28,841.89]
let pageHeight = contentWidth / 592.28 * 841.89;
//未生成pdf的html頁面高度
let leftHeight = contentHeight;
//pdf頁面偏移
let position = 0;
//html頁面生成的canvas在pdf中圖片的寬高
let imgWidth = 595.28;
let imgHeight = 592.28 / contentWidth * contentHeight;
let pageData = canvas.toDataURL('image/jpeg', 1.0);
let pdf = new jsPDF('', 'pt', 'a4');
//有兩個高度須要區分,一個是html頁面的實際高度,和生成pdf的頁面高度(841.89)
//當內容未超過pdf一頁顯示的範圍,無需分頁
if (leftHeight < pageHeight) {
pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
} else {
while (leftHeight > 0) {
pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
leftHeight -= pageHeight;
position -= 841.89;
//避免添加空白頁
if (leftHeight > 0) {
pdf.addPage();
}
}
}
pdf.save(fileName);
}
)
}
}
export default{
install
}
複製代碼