Browse Source

2026-10-26

导出优化
master
fengyuan_yang 2 weeks ago
parent
commit
2dd9e33733
  1. 307
      src/components/JsonExcel.vue
  2. 2
      src/main.js
  3. 40
      src/views/modules/project/projectInfo/projectInfo.vue
  4. 35
      src/views/modules/proofing/requestForProofing.vue

307
src/components/JsonExcel.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>

2
src/main.js

@ -13,7 +13,7 @@ import httpRequest from '@/utils/httpRequest' // api: https://github.com/axios/a
import { isAuth } from '@/utils'
import { accessField } from '@/utils'
import cloneDeep from 'lodash/cloneDeep'
import JsonExcel from 'vue-json-excel'
import JsonExcel from '@/components/JsonExcel.vue'
import i18n from '@/i18n/i18n'
import './utils/directives'
import decimalUtil from '@/utils/decimalUtil.js'

40
src/views/modules/project/projectInfo/projectInfo.vue

@ -479,6 +479,8 @@
addOrUpdateVisible:false,
filterVisible:false,
isFilterSearch: false,
listQueried: false,
exportParams: null,
filterSearchData: {},
nodeOptions: [],
approvalList: [],
@ -1594,15 +1596,30 @@
})
},
//导出excel
//导出excel:仅导出已经查询出的全部数据,未查询时不导出
async createExportData() {
this.searchData.limit = -1
this.searchData.page = 1
await projectInfoSearch(this.searchData).then(({data}) => {
this.exportList= data.page.list;
if (!this.listQueried) {
return []
}
if (this.isFilterSearch) {
const params = Object.assign({}, this.filterSearchData, {
no: 1,
size: -1,
site: this.$store.state.user.site,
userName: this.$store.state.user.name,
userId: String(this.$store.state.user.id)
})
const {data} = await projectInfoSearchByAnyField(params)
return (data && data.page && data.page.list) || []
}
const params = Object.assign({}, this.exportParams || this.searchData, {
page: 1,
limit: -1,
userId: this.$store.state.user.id,
menuId: this.menuId
})
return this.exportList;
const {data} = await projectInfoSearch(params)
return (data && data.page && data.page.list) || []
},
startDownload() {
@ -1669,6 +1686,11 @@
this.searchData.page = this.pageIndex
this.searchData.userId = this.$store.state.user.id
this.searchData.menuId = this.menuId
if (!keepFilter) {
this.isFilterSearch = false
}
this.listQueried = true
this.exportParams = Object.assign({}, this.searchData)
projectInfoSearch(this.searchData).then(({data}) => {
if (data.code == 0) {
this.dataList1 = data.page.list
@ -2532,6 +2554,7 @@
this.filterSearchData = params
this.filterVisible = false
this.isFilterSearch = true
this.listQueried = true
},
hasProjectManager (source) {
if (!source) {
@ -2584,6 +2607,9 @@
this.searchData.page = this.pageIndex
this.searchData.userId = this.$store.state.user.id
this.searchData.menuId = this.menuId
this.listQueried = true
this.exportParams = Object.assign({}, this.searchData)
this.isFilterSearch = false
projectInfoSearch(this.searchData).then(({data}) => {
if (data.code == 0) {
this.dataList1 = data.page.list

35
src/views/modules/proofing/requestForProofing.vue

@ -1027,6 +1027,8 @@ export default {
},
// 其它
exportLoading: false,
listQueried: false,
exportParams: null,
dataListLoading: false,
// 初始页签
activeTable: 'attribute',
@ -2334,6 +2336,9 @@ export default {
menuId: this.$route.meta.menuId
}
this.dataListLoading = true
this.listQueried = true
this.isFilterSearch = false
this.exportParams = Object.assign({}, params)
proofingInformationSearch(params).then(({data}) => {
if (data && data.code === 0) {
this.dataList = data.page.list
@ -2492,6 +2497,9 @@ export default {
...this.searchData,
createBy: this.$store.state.user.name,
}
this.listQueried = true
this.isFilterSearch = false
this.exportParams = Object.assign({}, params)
proofingInformationSearch(params).then(({data}) => {
if (data.code === 0) {
this.dataList = data.page.list
@ -3620,12 +3628,28 @@ export default {
* 导出excel
*/
async createExportData() {
this.searchData.limit = -1
this.searchData.page = 1
await proofingInformationSearch(this.searchData).then(({data}) => {
this.resultList = data.page.list
if (!this.listQueried) {
return []
}
if (this.isFilterSearch) {
const params = Object.assign({}, this.filterSearchData, {
site: this.$store.state.user.site,
menuId: '103001',
no: 1,
size: -1,
userId: String(this.$store.state.user.id),
userName: this.$store.state.user.name
})
const {data} = await proofingInformationSearchAny(params)
return (data && data.page && data.page.list) || []
}
const params = Object.assign({}, this.exportParams || this.searchData, {
page: 1,
limit: -1,
createBy: this.$store.state.user.name
})
return this.resultList
const {data} = await proofingInformationSearch(params)
return (data && data.page && data.page.list) || []
},
startDownload() {
this.exportLoading = true
@ -4173,6 +4197,7 @@ export default {
})
this.filterSearchData = params
this.isFilterSearch = true
this.listQueried = true
this.filterVisible = false
},

Loading…
Cancel
Save