UNPKG

vue3-json-excel

Version:

a vue3 project for json to excel

496 lines (440 loc) 16.2 kB
(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; }));