4 changed files with 371 additions and 13 deletions
-
307src/components/JsonExcel.vue
-
2src/main.js
-
40src/views/modules/project/projectInfo/projectInfo.vue
-
35src/views/modules/proofing/requestForProofing.vue
@ -0,0 +1,307 @@ |
|||
<template> |
|||
<div :id="idName" @click="generate"> |
|||
<slot> Download {{ name }} </slot> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as XLSXNS from "xlsx"; |
|||
|
|||
const XLSX = XLSXNS.default || XLSXNS; |
|||
|
|||
export default { |
|||
props: { |
|||
type: { |
|||
type: String, |
|||
default: "xls", |
|||
}, |
|||
data: { |
|||
type: Array, |
|||
required: false, |
|||
default: null, |
|||
}, |
|||
fields: { |
|||
type: Object, |
|||
default: () => null, |
|||
}, |
|||
exportFields: { |
|||
type: Object, |
|||
default: () => null, |
|||
}, |
|||
defaultValue: { |
|||
type: String, |
|||
required: false, |
|||
default: "", |
|||
}, |
|||
header: { |
|||
default: null, |
|||
}, |
|||
footer: { |
|||
default: null, |
|||
}, |
|||
name: { |
|||
type: String, |
|||
default: "data.xls", |
|||
}, |
|||
fetch: { |
|||
type: Function, |
|||
}, |
|||
meta: { |
|||
type: Array, |
|||
default: () => [], |
|||
}, |
|||
worksheet: { |
|||
type: String, |
|||
default: "Sheet1", |
|||
}, |
|||
beforeGenerate: { |
|||
type: Function, |
|||
}, |
|||
beforeFinish: { |
|||
type: Function, |
|||
}, |
|||
escapeCsv: { |
|||
type: Boolean, |
|||
default: true, |
|||
}, |
|||
stringifyLongNum: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
}, |
|||
computed: { |
|||
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 === "csv") { |
|||
return this.exportText( |
|||
this.jsonToCSV(json), |
|||
this.buildFileName("csv"), |
|||
"text/csv;charset=utf-8" |
|||
); |
|||
} |
|||
return this.exportWorkbook(this.jsonToWorkbook(json), this.buildFileName("xlsx")); |
|||
}, |
|||
pageTitle() { |
|||
const title = this.$route && this.$route.meta && this.$route.meta.title; |
|||
const text = title == null ? "" : String(title).trim(); |
|||
if (!text || text.indexOf(".") >= 0) return ""; |
|||
return text; |
|||
}, |
|||
buildFileName(ext) { |
|||
const now = this.dayjs().format("YYYYMMDDHHmmss"); |
|||
const title = this.pageTitle(); |
|||
let raw = String(this.name || "").replace(/\.xlsx?$/i, "").replace(/\.csv$/i, "").trim(); |
|||
const prefix = raw.replace(/\d{14}$/, "").replace(/[_\-\s]+$/, ""); |
|||
const base = (title || prefix || "导出") + now; |
|||
return base.replace(/[\\/:*?"<>|]/g, "_") + "." + ext; |
|||
}, |
|||
safeSheetName(name) { |
|||
const text = String(name || "Sheet1").replace(/[:\\/?*\[\]]/g, " ").trim() || "Sheet1"; |
|||
return text.slice(0, 31); |
|||
}, |
|||
titleRows() { |
|||
const header = this.header || this.$attrs.title; |
|||
if (header == null || (Array.isArray(header) && header.length === 0)) { |
|||
return []; |
|||
} |
|||
const rows = Array.isArray(header) ? header.filter((item) => item) : [header]; |
|||
const title = this.pageTitle(); |
|||
if (title && rows.length === 1) { |
|||
return [title]; |
|||
} |
|||
return rows; |
|||
}, |
|||
exportWorkbook(workbook, filename) { |
|||
const output = XLSX.write(workbook, { bookType: "xlsx", type: "array" }); |
|||
const blob = new Blob([output], { |
|||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
|||
}); |
|||
this.saveBlob(blob, filename); |
|||
}, |
|||
exportText(data, filename, mime) { |
|||
const blob = new Blob(["\ufeff" + data], { type: mime }); |
|||
this.saveBlob(blob, filename); |
|||
}, |
|||
async saveBlob(blob, filename) { |
|||
if (typeof this.beforeFinish === "function") await this.beforeFinish(); |
|||
const link = document.createElement("a"); |
|||
const url = window.URL.createObjectURL(blob); |
|||
link.href = url; |
|||
link.download = filename; |
|||
link.style.display = "none"; |
|||
document.body.appendChild(link); |
|||
link.click(); |
|||
document.body.removeChild(link); |
|||
window.URL.revokeObjectURL(url); |
|||
}, |
|||
jsonToWorkbook(data) { |
|||
const labels = Object.keys(data[0] || {}); |
|||
const rows = []; |
|||
const titles = this.titleRows(); |
|||
titles.forEach((text) => rows.push([text])); |
|||
rows.push(labels); |
|||
data.forEach((item) => { |
|||
rows.push(labels.map((key) => this.cellValue(item[key]))); |
|||
}); |
|||
if (this.footer != null && !(Array.isArray(this.footer) && this.footer.length === 0)) { |
|||
const footerRows = Array.isArray(this.footer) ? this.footer : [this.footer]; |
|||
footerRows.forEach((text) => { |
|||
if (text) rows.push([text]); |
|||
}); |
|||
} |
|||
const sheet = XLSX.utils.aoa_to_sheet(rows); |
|||
if (labels.length > 1 && titles.length) { |
|||
sheet["!merges"] = titles.map((_, index) => ({ |
|||
s: { r: index, c: 0 }, |
|||
e: { r: index, c: labels.length - 1 }, |
|||
})); |
|||
} |
|||
const workbook = XLSX.utils.book_new(); |
|||
const sheetName = this.safeSheetName(this.pageTitle() || this.worksheet); |
|||
XLSX.utils.book_append_sheet(workbook, sheet, sheetName); |
|||
return workbook; |
|||
}, |
|||
cellValue(value) { |
|||
if (typeof value === "string") { |
|||
return this.preprocessLongNum(value.replace(/<br\s*\/?>/gi, "\n")); |
|||
} |
|||
return this.preprocessLongNum(value); |
|||
}, |
|||
jsonToCSV(data) { |
|||
let _self = this; |
|||
var csvData = []; |
|||
const header = this.titleRows(); |
|||
if (header.length) { |
|||
csvData.push(this.parseExtraData(header, "${data}\r\n")); |
|||
} |
|||
for (let key in data[0]) { |
|||
csvData.push(key); |
|||
csvData.push(","); |
|||
} |
|||
csvData.pop(); |
|||
csvData.push("\r\n"); |
|||
data.map(function (item) { |
|||
for (let key in item) { |
|||
let escapedCSV = item[key] + ""; |
|||
if (_self.escapeCsv) { |
|||
escapedCSV = '="' + escapedCSV + '"'; |
|||
if (escapedCSV.match(/[,"\n]/)) { |
|||
escapedCSV = '"' + escapedCSV.replace(/\"/g, '""') + '"'; |
|||
} |
|||
} |
|||
csvData.push(escapedCSV); |
|||
csvData.push(","); |
|||
} |
|||
csvData.pop(); |
|||
csvData.push("\r\n"); |
|||
}); |
|||
if (this.footer != null) { |
|||
csvData.push(this.parseExtraData(this.footer, "${data}\r\n")); |
|||
} |
|||
return csvData.join(""); |
|||
}, |
|||
getProcessedJson(data, header) { |
|||
let keys = this.getKeys(data, header); |
|||
let newData = []; |
|||
let _self = this; |
|||
data.map(function (item) { |
|||
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(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; |
|||
}, |
|||
preprocessLongNum(value) { |
|||
if (this.stringifyLongNum) { |
|||
if (String(value).startsWith("0x")) { |
|||
return value; |
|||
} |
|||
if (!isNaN(value) && value != "") { |
|||
if (value > 99999999999 || value < 0.0000000000001) { |
|||
return String(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; |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue