vue3-json-excel
Version:
a vue3 project for json to excel
496 lines (440 loc) • 16.2 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('vue')) :
typeof define === 'function' && define.amd ? define(['vue'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.JsonExcel = factory(global.vue));
})(this, (function (vue) { 'use strict';
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var download = createCommonjsModule(function (module, exports) {
//download.js v4.2, by dandavis; 2008-2016. [MIT] see http://danml.com/download.html for tests/usage
// v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and optional mime
// v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for larger+faster saves than dataURLs
// v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support. 3.1 improved safari handling.
// v4 adds AMD/UMD, commonJS, and plain browser support
// v4.1 adds url download capability via solo URL argument (same domain/CORS only)
// v4.2 adds semantic variable names, long (over 2MB) dataURL support, and hidden by default temp anchors
// https://github.com/rndme/download
(function (root, factory) {
{
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
}
}(commonjsGlobal, function () {
return function download(data, strFileName, strMimeType) {
var self = window, // this script is only for browsers anyway...
defaultMime = "application/octet-stream", // this default mime also triggers iframe downloads
mimeType = strMimeType || defaultMime,
payload = data,
url = !strFileName && !strMimeType && payload,
anchor = document.createElement("a"),
toString = function(a){return String(a);},
myBlob = (self.Blob || self.MozBlob || self.WebKitBlob || toString),
fileName = strFileName || "download",
blob,
reader;
myBlob= myBlob.call ? myBlob.bind(self) : Blob ;
if(String(this)==="true"){ //reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback
payload=[payload, mimeType];
mimeType=payload[0];
payload=payload[1];
}
if(url && url.length< 2048){ // if no filename and no mime, assume a url was passed as the only argument
fileName = url.split("/").pop().split("?")[0];
anchor.href = url; // assign href prop to temp anchor
if(anchor.href.indexOf(url) !== -1){ // if the browser determines that it's a potentially valid url path:
var ajax=new XMLHttpRequest();
ajax.open( "GET", url, true);
ajax.responseType = 'blob';
ajax.onload= function(e){
download(e.target.response, fileName, defaultMime);
};
setTimeout(function(){ ajax.send();}, 0); // allows setting custom ajax headers using the return:
return ajax;
} // end if valid url?
} // end if url?
//go ahead and download dataURLs right away
if(/^data:([\w+-]+\/[\w+.-]+)?[,;]/.test(payload)){
if(payload.length > (1024*1024*1.999) && myBlob !== toString ){
payload=dataUrlToBlob(payload);
mimeType=payload.type || defaultMime;
}else {
return navigator.msSaveBlob ? // IE10 can't do a[download], only Blobs:
navigator.msSaveBlob(dataUrlToBlob(payload), fileName) :
saver(payload) ; // everyone else can save dataURLs un-processed
}
}else {//not data url, is it a string with special needs?
if(/([\x80-\xff])/.test(payload)){
var i=0, tempUiArr= new Uint8Array(payload.length), mx=tempUiArr.length;
for(i;i<mx;++i) tempUiArr[i]= payload.charCodeAt(i);
payload=new myBlob([tempUiArr], {type: mimeType});
}
}
blob = payload instanceof myBlob ?
payload :
new myBlob([payload], {type: mimeType}) ;
function dataUrlToBlob(strUrl) {
var parts= strUrl.split(/[:;,]/),
type= parts[1],
decoder= parts[2] == "base64" ? atob : decodeURIComponent,
binData= decoder( parts.pop() ),
mx= binData.length,
i= 0,
uiArr= new Uint8Array(mx);
for(i;i<mx;++i) uiArr[i]= binData.charCodeAt(i);
return new myBlob([uiArr], {type: type});
}
function saver(url, winMode){
if ('download' in anchor) { //html5 A[download]
anchor.href = url;
anchor.setAttribute("download", fileName);
anchor.className = "download-js-link";
anchor.innerHTML = "downloading...";
anchor.style.display = "none";
document.body.appendChild(anchor);
setTimeout(function() {
anchor.click();
document.body.removeChild(anchor);
if(winMode===true){setTimeout(function(){ self.URL.revokeObjectURL(anchor.href);}, 250 );}
}, 66);
return true;
}
// handle non-a[download] safari as best we can:
if(/(Version)\/(\d+)\.(\d+)(?:\.(\d+))?.*Safari\//.test(navigator.userAgent)) {
if(/^data:/.test(url)) url="data:"+url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
if(!window.open(url)){ // popup blocked, offer direct download:
if(confirm("Displaying New Document\n\nUse Save As... to download, then click back to return to this page.")){ location.href=url; }
}
return true;
}
//do iframe dataURL download (old ch+FF):
var f = document.createElement("iframe");
document.body.appendChild(f);
if(!winMode && /^data:/.test(url)){ // force a mime that will download:
url="data:"+url.replace(/^data:([\w\/\-\+]+)/, defaultMime);
}
f.src=url;
setTimeout(function(){ document.body.removeChild(f); }, 333);
}//end saver
if (navigator.msSaveBlob) { // IE10+ : (has Blob, but not a[download] or URL)
return navigator.msSaveBlob(blob, fileName);
}
if(self.URL){ // simple fast and modern way using Blob and URL:
saver(self.URL.createObjectURL(blob), true);
}else {
// handle non-Blob()+non-URL browsers:
if(typeof blob === "string" || blob.constructor===toString ){
try{
return saver( "data:" + mimeType + ";base64," + self.btoa(blob) );
}catch(y){
return saver( "data:" + mimeType + "," + encodeURIComponent(blob) );
}
}
// Blob but not URL support:
reader=new FileReader();
reader.onload=function(e){
saver(this.result);
};
reader.readAsDataURL(blob);
}
return true;
}; /* end download() */
}));
});
var script = {
name: 'vue3-json-excel',
props: {
// mime type [xls, csv]
type: {
type: String,
default: "xls",
},
// Json to download
jsonData: {
type: Array,
required: false,
default: null,
},
// fields inside the Json Object that you want to export
// if no given, all the properties in the Json are exported
fields: {
type: Object,
default: () => null,
},
// this prop is used to fix the problem with other components that use the
// variable fields, like vee-validate. exportFields works exactly like fields
exportFields: {
type: Object,
default: () => null,
},
// Use as fallback when the row has no field values
defaultValue: {
type: String,
required: false,
default: "",
},
// Title(s) for the data, could be a string or an array of strings (multiple titles)
header: {
default: '',
},
// Footer(s) for the data, could be a string or an array of strings (multiple footers)
footer: {
default: '',
},
// filename to export
name: {
type: String,
default: "jsonData.xls",
},
fetch: {
type: Function,
},
meta: {
type: Array,
default: () => [],
},
worksheet: {
type: String,
default: "表1",
},
//event before generate was called
beforeGenerate: {
type: Function,
},
//event before download pops up
beforeFinish: {
type: Function,
},
// Determine if CSV Data should be escaped
escapeCsv: {
type: Boolean,
default: true,
},
// long number stringify
stringifyLongNum: {
type: Boolean,
default: false,
},
},
setup(props) {
// props
const { type, fields, exportFields,
beforeFinish, header, footer,
stringifyLongNum, beforeGenerate, jsonData,
fetch, defaultValue, name, worksheet } = vue.toRefs(props);
// computed
const idName = vue.computed(() => {
var now = new Date().getTime();
return "export_" + now;
});
const downloadFields = vue.computed(() => {
if (fields) return vue.toRaw(fields.value)
if(exportFields) return exportFields.value()
});
// methods
const export1 = async(data1, filename, mine) => {
let blob = base64ToBlob(data1, mine);
if (typeof beforeFinish === "function") await beforeFinish();
download(blob, filename, mine);
};
const base64ToBlob = (data1, mine) => {
let base64 = window.btoa(window.unescape(encodeURIComponent(data1)));
let bstr = atob(base64);
let n = bstr.length;
let u8arr = new Uint8ClampedArray(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mine });
};
const jsonToXLS = (data1) => {
let xlsTemp =
'<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta name=ProgId content=Excel.Sheet> <meta name=Generator content="Microsoft Excel 11"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>${worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--><style>br {mso-data-placement: same-cell;}</style></head><body><table>${table}</table></body></html>';
let xlsData = "<thead>";
const colspan = Object.keys(data1[0]).length;
//Header
if (header.value) {
xlsData += parseExtraData(
header.value,
'<tr><th colspan="' + colspan + '">${data}</th></tr>'
);
}
//Fields
xlsData += "<tr>";
for (let key in data1[0]) {
xlsData += "<th>" + key + "</th>";
}
xlsData += "</tr>";
xlsData += "</thead>";
//Data
xlsData += "<tbody>";
data1.map(function (item, index) {
xlsData += "<tr>";
for (let key in item) {
xlsData +=
"<td>" +
preprocessLongNum(
valueReformattedForMultilines(item[key])
) +
"</td>";
}
xlsData += "</tr>";
});
xlsData += "</tbody>";
//Footer
if (footer.value) {
xlsData += "<tfoot>";
xlsData += parseExtraData(
footer.value,
'<tr><td colspan="' + colspan + '">${data}</td></tr>'
);
xlsData += "</tfoot>";
}
return xlsTemp
.replace("${table}", xlsData)
.replace("${worksheet}", worksheet.value);
};
const parseExtraData = (extraData, format) => {
let parseData = "";
if (Array.isArray(extraData)) {
for (var i = 0; i < extraData.length; i++) {
if (extraData[i])
parseData += format.replace("${data}", extraData[i]);
}
} else {
parseData += format.replace("${data}", extraData);
}
return parseData;
};
const preprocessLongNum = (value) => {
if (stringifyLongNum.value) {
if (String(value).startsWith("0x")) {
return value;
}
if (!isNaN(value) && value != "") {
if (value > 99999999999 || value < 0.0000000000001) {
return '="' + value + '"';
}
}
}
return value;
};
const valueReformattedForMultilines = (value) => {
if (typeof value == "string") return value.replace(/\n/gi, "<br/>");
else return value;
};
const getKeys = (data1, header1) => {
if (header1) {
return header1;
}
let keys = {};
for (let key in data1[0]) {
keys[key] = key;
}
return keys;
};
const parseValue = (value) => {
return value || value === 0 || typeof value === "boolean"
? value
: defaultValue.value;
};
const getValueFromNestedItem = (item, indexes) => {
let nestedItem = item;
for (let index of indexes) {
if (nestedItem) nestedItem = nestedItem[index];
}
return parseValue(nestedItem);
};
const getValueFromCallback = (item, callback) => {
if (typeof callback !== "function") return defaultValue.value;
const value = callback(item);
return parseValue(value);
};
const getValue = (key, item) => {
const field = typeof key !== "object" ? key : key.field;
let indexes = typeof field !== "string" ? [] : field.split(".");
let value = defaultValue.value;
if (!field) value = item;
else if (indexes.length > 1)
value = getValueFromNestedItem(item, indexes);
else value = parseValue(item[field]);
if (key.hasOwnProperty("callback"))
value = getValueFromCallback(value, key.callback);
return value;
};
const getProcessedJson = (data1, header1) => {
let keys = getKeys(data1, header1).value;
let newData = [];
data1.map(function (item, index) {
let newItem = {};
for (let label in keys) {
let property = keys[label];
newItem[label] = getValue(property, item);
}
newData.push(newItem);
});
return newData;
};
const generate = async () => {
if (beforeGenerate && typeof beforeGenerate.value === "function") {
await beforeGenerate.value();
}
let data2 = jsonData && vue.toRaw(jsonData.value);
if ((fetch && typeof fetch.value === "function") || !jsonData) {
data2 = await fetch.value();
}
if(data2.length === 0) {
console.warn('无导出数据');
return
}
const DATA = vue.toRaw(data2);
let json = getProcessedJson(DATA, downloadFields);
if (type.value === "html") {
// this is mainly for testing
return export1(
jsonToXLS(json),
name.value.replace(".xls", ".html"),
"text/html"
);
}
return export1(
jsonToXLS(json),
name.value,
"application/vnd.ms-excel"
);
};
return {
idName,
generate,
name
}
}
};
const _hoisted_1 = ["id"];
function render(_ctx, _cache, $props, $setup, $data, $options) {
return (vue.openBlock(), vue.createElementBlock("span", {
id: $setup.idName,
onClick: _cache[0] || (_cache[0] = (...args) => ($setup.generate && $setup.generate(...args)))
}, [
vue.renderSlot(_ctx.$slots, "default", {}, () => [
vue.createTextVNode(" Download " + vue.toDisplayString($setup.name), 1 /* TEXT */)
])
], 8 /* PROPS */, _hoisted_1))
}
script.render = render;
script.__file = "src/components/json-excel.vue";
var version = "1.0.10-alpha";
const VERSION = version;
console.log('version:'+ VERSION);
const install=(app)=>{
app.component(script.name, script );
};
var index = {
Vue3JsonExcel: script,
install
};
return index;
}));