5 changed files with 380 additions and 2 deletions
-
369src/components/JsonExcel.vue
-
2src/main.js
-
6src/utils/excel-util.js
-
1src/views/modules/print/rePrintPoPart.vue
-
4src/views/modules/warehouse/labelTransactionLog.vue
@ -0,0 +1,369 @@ |
|||||
|
<template> |
||||
|
<div :id="idName" @click="generate"> |
||||
|
<slot> Download {{ name }} </slot> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import download from "downloadjs"; |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
// mime type [xls, csv] |
||||
|
type: { |
||||
|
type: String, |
||||
|
default: "xls", |
||||
|
}, |
||||
|
// Json to download |
||||
|
data: { |
||||
|
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: null, |
||||
|
}, |
||||
|
// Footer(s) for the data, could be a string or an array of strings (multiple footers) |
||||
|
footer: { |
||||
|
default: null, |
||||
|
}, |
||||
|
// filename to export |
||||
|
name: { |
||||
|
type: String, |
||||
|
default: "data.xls", |
||||
|
}, |
||||
|
fetch: { |
||||
|
type: Function, |
||||
|
}, |
||||
|
meta: { |
||||
|
type: Array, |
||||
|
default: () => [], |
||||
|
}, |
||||
|
worksheet: { |
||||
|
type: String, |
||||
|
default: "Sheet1", |
||||
|
}, |
||||
|
//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, |
||||
|
}, |
||||
|
}, |
||||
|
computed: { |
||||
|
// unique identifier |
||||
|
idName() { |
||||
|
var now = new Date().getTime(); |
||||
|
return "export_" + now; |
||||
|
}, |
||||
|
|
||||
|
downloadFields() { |
||||
|
if (this.fields) return this.fields; |
||||
|
|
||||
|
if (this.exportFields) return this.exportFields; |
||||
|
}, |
||||
|
}, |
||||
|
methods: { |
||||
|
async generate() { |
||||
|
if (typeof this.beforeGenerate === "function") { |
||||
|
await this.beforeGenerate(); |
||||
|
} |
||||
|
let data = this.data; |
||||
|
try { |
||||
|
if (typeof this.fetch === "function" || !data) data = await this.fetch(); |
||||
|
} catch (e) { |
||||
|
if (typeof this.beforeFinish === "function") await this.beforeFinish(); |
||||
|
throw e; |
||||
|
} |
||||
|
|
||||
|
if (!data || !data.length) { |
||||
|
if (typeof this.beforeFinish === "function") await this.beforeFinish(); |
||||
|
if (this.$message) { |
||||
|
this.$message.warning('没有数据可导出'); |
||||
|
} |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
let json = this.getProcessedJson(data, this.downloadFields); |
||||
|
if (this.type === "html") { |
||||
|
// this is mainly for testing |
||||
|
return this.export( |
||||
|
this.jsonToXLS(json), |
||||
|
this.name.replace(".xls", ".html"), |
||||
|
"text/html" |
||||
|
); |
||||
|
} else if (this.type === "csv") { |
||||
|
return this.export( |
||||
|
this.jsonToCSV(json), |
||||
|
this.name.replace(".xls", ".csv"), |
||||
|
"application/csv" |
||||
|
); |
||||
|
} |
||||
|
return this.export( |
||||
|
this.jsonToXLS(json), |
||||
|
this.name, |
||||
|
"application/vnd.ms-excel" |
||||
|
); |
||||
|
}, |
||||
|
/* |
||||
|
Use downloadjs to generate the download link |
||||
|
*/ |
||||
|
export: async function (data, filename, mime) { |
||||
|
let blob = this.base64ToBlob(data, mime); |
||||
|
if (typeof this.beforeFinish === "function") await this.beforeFinish(); |
||||
|
download(blob, filename, mime); |
||||
|
}, |
||||
|
/* |
||||
|
jsonToXLS |
||||
|
--------------- |
||||
|
Transform json data into an xml document with MS Excel format, sadly |
||||
|
it shows a prompt when it opens, that is a default behavior for |
||||
|
Microsoft office and cannot be avoided. It's recommended to use CSV format instead. |
||||
|
*/ |
||||
|
jsonToXLS(data) { |
||||
|
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(data[0]).length; |
||||
|
let _self = this; |
||||
|
|
||||
|
//Header |
||||
|
const header = this.header || this.$attrs.title; |
||||
|
if (header) { |
||||
|
xlsData += this.parseExtraData( |
||||
|
header, |
||||
|
'<tr><th colspan="' + colspan + '">${data}</th></tr>' |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
//Fields |
||||
|
xlsData += "<tr>"; |
||||
|
for (let key in data[0]) { |
||||
|
xlsData += "<th>" + key + "</th>"; |
||||
|
} |
||||
|
xlsData += "</tr>"; |
||||
|
xlsData += "</thead>"; |
||||
|
|
||||
|
//Data |
||||
|
xlsData += "<tbody>"; |
||||
|
data.map(function (item, index) { |
||||
|
xlsData += "<tr>"; |
||||
|
for (let key in item) { |
||||
|
xlsData += |
||||
|
"<td>" + |
||||
|
_self.preprocessLongNum( |
||||
|
_self.valueReformattedForMultilines(item[key]) |
||||
|
) + |
||||
|
"</td>"; |
||||
|
} |
||||
|
xlsData += "</tr>"; |
||||
|
}); |
||||
|
xlsData += "</tbody>"; |
||||
|
|
||||
|
//Footer |
||||
|
if (this.footer != null) { |
||||
|
xlsData += "<tfoot>"; |
||||
|
xlsData += this.parseExtraData( |
||||
|
this.footer, |
||||
|
'<tr><td colspan="' + colspan + '">${data}</td></tr>' |
||||
|
); |
||||
|
xlsData += "</tfoot>"; |
||||
|
} |
||||
|
|
||||
|
return xlsTemp |
||||
|
.replace("${table}", xlsData) |
||||
|
.replace("${worksheet}", this.worksheet); |
||||
|
}, |
||||
|
/* |
||||
|
jsonToCSV |
||||
|
--------------- |
||||
|
Transform json data into an CSV file. |
||||
|
*/ |
||||
|
jsonToCSV(data) { |
||||
|
let _self = this; |
||||
|
var csvData = []; |
||||
|
|
||||
|
//Header |
||||
|
const header = this.header || this.$attrs.title; |
||||
|
if (header) { |
||||
|
csvData.push(this.parseExtraData(header, "${data}\r\n")); |
||||
|
} |
||||
|
|
||||
|
//Fields |
||||
|
for (let key in data[0]) { |
||||
|
csvData.push(key); |
||||
|
csvData.push(","); |
||||
|
} |
||||
|
csvData.pop(); |
||||
|
csvData.push("\r\n"); |
||||
|
//Data |
||||
|
data.map(function (item) { |
||||
|
for (let key in item) { |
||||
|
let escapedCSV = item[key] + ""; |
||||
|
// Escaped CSV data to string to avoid problems with numbers or other types of values |
||||
|
// this is controlled by the prop escapeCsv |
||||
|
if (_self.escapeCsv) { |
||||
|
escapedCSV = '="' + escapedCSV + '"'; // cast Numbers to string |
||||
|
if (escapedCSV.match(/[,"\n]/)) { |
||||
|
escapedCSV = '"' + escapedCSV.replace(/\"/g, '""') + '"'; |
||||
|
} |
||||
|
} |
||||
|
csvData.push(escapedCSV); |
||||
|
csvData.push(","); |
||||
|
} |
||||
|
csvData.pop(); |
||||
|
csvData.push("\r\n"); |
||||
|
}); |
||||
|
//Footer |
||||
|
if (this.footer != null) { |
||||
|
csvData.push(this.parseExtraData(this.footer, "${data}\r\n")); |
||||
|
} |
||||
|
return csvData.join(""); |
||||
|
}, |
||||
|
/* |
||||
|
getProcessedJson |
||||
|
--------------- |
||||
|
Get only the data to export, if no fields are set return all the data |
||||
|
*/ |
||||
|
getProcessedJson(data, header) { |
||||
|
let keys = this.getKeys(data, header); |
||||
|
let newData = []; |
||||
|
let _self = this; |
||||
|
data.map(function (item, index) { |
||||
|
let newItem = {}; |
||||
|
for (let label in keys) { |
||||
|
let property = keys[label]; |
||||
|
newItem[label] = _self.getValue(property, item); |
||||
|
} |
||||
|
newData.push(newItem); |
||||
|
}); |
||||
|
|
||||
|
return newData; |
||||
|
}, |
||||
|
getKeys(data, header) { |
||||
|
if (header) { |
||||
|
return header; |
||||
|
} |
||||
|
|
||||
|
let keys = {}; |
||||
|
for (let key in data[0]) { |
||||
|
keys[key] = key; |
||||
|
} |
||||
|
return keys; |
||||
|
}, |
||||
|
/* |
||||
|
parseExtraData |
||||
|
--------------- |
||||
|
Parse title and footer attribute to the csv format |
||||
|
*/ |
||||
|
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; |
||||
|
}, |
||||
|
|
||||
|
getValue(key, item) { |
||||
|
const field = typeof key !== "object" ? key : key.field; |
||||
|
let indexes = typeof field !== "string" ? [] : field.split("."); |
||||
|
let value = this.defaultValue; |
||||
|
|
||||
|
if (!field) value = item; |
||||
|
else if (indexes.length > 1) |
||||
|
value = this.getValueFromNestedItem(item, indexes); |
||||
|
else value = this.parseValue(item[field]); |
||||
|
|
||||
|
if (key.hasOwnProperty("callback")) |
||||
|
value = this.getValueFromCallback(value, key.callback); |
||||
|
|
||||
|
return value; |
||||
|
}, |
||||
|
|
||||
|
/* |
||||
|
convert values with newline \n characters into <br/> |
||||
|
*/ |
||||
|
valueReformattedForMultilines(value) { |
||||
|
if (typeof value == "string") return value.replace(/\n/gi, "<br/>"); |
||||
|
else return value; |
||||
|
}, |
||||
|
preprocessLongNum(value) { |
||||
|
if (this.stringifyLongNum) { |
||||
|
if (String(value).startsWith("0x")) { |
||||
|
return value; |
||||
|
} |
||||
|
if (!isNaN(value) && value != "") { |
||||
|
if (value > 99999999999 || value < 0.0000000000001) { |
||||
|
return '="' + value + '"'; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return value; |
||||
|
}, |
||||
|
getValueFromNestedItem(item, indexes) { |
||||
|
let nestedItem = item; |
||||
|
for (let index of indexes) { |
||||
|
if (nestedItem) nestedItem = nestedItem[index]; |
||||
|
} |
||||
|
return this.parseValue(nestedItem); |
||||
|
}, |
||||
|
|
||||
|
getValueFromCallback(item, callback) { |
||||
|
if (typeof callback !== "function") return this.defaultValue; |
||||
|
const value = callback(item); |
||||
|
return this.parseValue(value); |
||||
|
}, |
||||
|
parseValue(value) { |
||||
|
return value || value === 0 || typeof value === "boolean" |
||||
|
? value |
||||
|
: this.defaultValue; |
||||
|
}, |
||||
|
base64ToBlob(data, mime) { |
||||
|
let base64 = window.btoa(window.unescape(encodeURIComponent(data))); |
||||
|
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: mime }); |
||||
|
}, |
||||
|
}, // end methods |
||||
|
}; |
||||
|
</script> |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue