20260712212917

This commit is contained in:
oneao committed 2026-07-12 21:29:17 +08:00
1 parent 4677f6c519
commit 00472688ab
361 files changed
-153949

No files matched your search

@@ -1,465 +0,0 @@
<script setup>
import { ElMessage } from "element-plus";
import { computed, ref, watch } from "vue";
import { useUserStore } from "@/store/user";
import axios from "@/utils/axios";
import { compareAndClassifyData } from "@/utils/dataDiffUtils";
import { getTableColumns } from "@/utils/dodata";
import { downloadZip } from "@/utils/file";
const props = defineProps({
viewName: {
type: String,
required: true,
},
footerData: {
type: Array,
default: () => [],
},
searchCondition: {
type: String,
default: "",
},
orderField: {
type: String,
default: "",
},
limit: {
type: Number,
default: 10000,
},
total: {
type: Number,
default: 0,
},
});
const userStore = useUserStore();
const confirmLoading = ref(false);
const loading = ref(true);
const loadingTip = ref("正在加载中 . . .");
const step = ref(0);
const tableData = ref([]);
const oldTableData = ref([]);
// 计算总页数
const totalFiles = computed(() => {
const total = Number(props.total);
const limit = Number(props.limit);
if (Number.isNaN(total) || Number.isNaN(limit) || limit === 0) {
return 0;
}
return Math.ceil(total / limit);
});
// 计算每个文件的范围
const files = computed(() => {
const filesArr = [];
for (let i = 0; i < totalFiles.value; i++) {
const start = i * props.limit + 1;
const end = Math.min((i + 1) * props.limit, props.total);
filesArr.push({ start, end });
}
return filesArr;
});
async function onOpened() {
try {
// 获取数据
const res = await axios.get("/loadData", {
params: {
viewName: "s_columnexcel",
searchCondition: `col_TableName='${props.viewName}' AND userid = '${userStore.userInfo.userid}'`,
},
});
if (!res.data.success) {
ElMessage.warning(`加载失败:${res.data.message}`);
console.error(`加载失败:${res.data.message}`);
return;
}
if (res.data.data.length === 0) {
oldTableData.value = [];
// 当没有数据时,获取表格列数据
const columnRes = await getTableColumns("v_cw_zdorderfee_f_business");
if (!columnRes.success) {
ElMessage.warning(`加载失败: ${columnRes.error}`);
console.error(`加载失败: ${columnRes.error}`);
return;
}
columnRes.data.forEach((item) => {
tableData.value.push({
col_TableName: props.viewName,
col_FieldName: item.prop,
col_Caption: item.label,
col_Width: item.width,
col_Visible: "1",
});
});
} else {
tableData.value = res.data.data || [];
}
// 更新 tableData 中的 ShowSummary
tableData.value.forEach((item) => {
item.ShowSummary = props.footerData.some((footerItem) =>
Object.prototype.hasOwnProperty.call(footerItem, item.col_FieldName),
)
? "1"
: "0";
});
oldTableData.value = JSON.parse(JSON.stringify(tableData.value));
loading.value = false;
} catch (error) {
ElMessage.warning(`加载失败:${error.message}`);
console.error(`请求出错:${error.message}`);
}
}
async function handleConfirm() {
confirmLoading.value = true;
const { insertdata, updatedata, deletedata, hasChanges } = compareAndClassifyData(
tableData.value,
oldTableData.value,
"b_id",
["ShowSummary"],
);
if (hasChanges) {
const userid = userStore.userInfo.userid;
const modifiedInsertData = insertdata
.map((item) => ({
...item,
userid,
}))
.map((item) => {
delete item.ShowSummary;
return item;
});
const modifiedUpdateData = updatedata
.map((item) => ({
...item,
}))
.map((item) => {
delete item.ShowSummary;
return item;
});
const requestData = [
{
tableName: "s_columnexcel",
keyField: "b_id",
inserts: modifiedInsertData,
updates: modifiedUpdateData,
deletes: [],
},
];
try {
const response = await axios.post("/saveobjectV2", requestData, {
headers: { "Content-Type": "application/json" },
});
if (response.data.success) {
confirmLoading.value = false;
step.value = 1;
oldTableData.value = JSON.parse(JSON.stringify(tableData.value));
} else {
ElMessage.error(`保存导出所有页配置失败:${response.data.message}`);
}
} catch (error) {
ElMessage.error(`保存导出所有页配置失败:${error.message}`);
} finally {
confirmLoading.value = false;
}
} else {
confirmLoading.value = false;
step.value = 1;
}
}
function onClosed() {
loading.value = true;
loadingTip.value = "正在加载中 . . .";
step.value = 0;
tableData.value = [];
oldTableData.value = [];
}
async function handleExportExcel(start, end) {
try {
loading.value = true;
loadingTip.value = "正在导出中 . . .";
const exportColumns = tableData.value.filter((item) => item.col_Visible === "1");
const response = await axios.post(
"/excel/exportAll",
{
viewName: props.viewName,
columns: exportColumns,
searchCondition: props.searchCondition,
orderField: props.orderField,
minIndex: start - 1,
maxIndex: end - 1,
},
{ responseType: "blob" },
);
downloadFromResponse(response, "excel", "");
ElMessage.success("导出成功!");
loading.value = false;
} catch (error) {
// 处理请求失败的情况
ElMessage.warning(`导出失败:${error.message}`);
console.error("导出出错", error);
}
}
function downloadFromResponse(res, type, defaultFileName = "") {
let blob;
let disposition;
if (res.data && res.headers) {
blob = new Blob([res.data], {
type: res.headers["content-type"] || "application/octet-stream",
});
disposition = res.headers["content-disposition"];
} else {
blob = res instanceof Blob ? res : new Blob([res]);
disposition = res.headers ? res.headers.get("Content-Disposition") : null;
}
let fileName = defaultFileName || "";
// 判断 Content-Disposition 中的文件名
if (disposition) {
// 匹配 filename* 和 filename 格式
const fileNameMatch = disposition.match(/filename\*?=\s*([^;]+)/i); // 兼容 filename 和 filename* 格式
if (fileNameMatch && fileNameMatch[1]) {
// 如果匹配到的文件名包含编码,解码它
fileName = decodeURIComponent(fileNameMatch[1].replace(/^UTF-8''/, ""));
}
}
const blobUrl = window.URL.createObjectURL(blob);
if (type === "preview") {
// 预览:新窗口打开
window.open(blobUrl, "_blank");
} else {
// 下载
const link = document.createElement("a");
link.href = blobUrl;
if (fileName) link.download = fileName;
document.body.appendChild(link);
link.click();
link.remove();
}
// 释放 URL
window.URL.revokeObjectURL(blobUrl);
}
async function handleDownloadZip() {
try {
loading.value = true;
loadingTip.value = "正在下载中 . . .";
const filesToDownload = [];
for (let i = 0; i < files.value.length; i++) {
loadingTip.value = `正在下载第 ${i + 1} 个. . .`;
const file = files.value[i];
const { start, end } = file;
// 通过 axios 获取文件数据
const response = await axios.post(
"/excel/exportAll",
{
viewName: props.viewName,
columns: tableData.value.filter((item) => item.col_Visible === "1"),
searchCondition: props.searchCondition,
orderField: props.orderField,
minIndex: start - 1,
maxIndex: end - 1,
},
{ responseType: "blob" },
);
// 生成临时 URL
const blobUrl = window.URL.createObjectURL(response.data);
// 收集文件的名称和临时 URL
filesToDownload.push({
mx_filename: `${start}-${end}.xlsx`,
mx_mapfilename: blobUrl,
});
}
// 调用 downloadZip 方法,传递文件列表
await downloadZip(filesToDownload, "所有页合计");
// 释放生成的临时 URL
filesToDownload.forEach((file) => {
window.URL.revokeObjectURL(file.url);
});
loading.value = false;
ElMessage.success("压缩包下载成功!");
} catch (error) {
ElMessage.warning(`下载失败:${error.message}`);
console.error("下载出错", error);
}
}
</script>
<template>
<el-dialog title="导出所有页" width="500" v-bind="$attrs" @closed="onClosed" @opened="onOpened">
<div v-loading="loading" class="g3-container" :element-loading-text="loadingTip">
<vxe-table
v-if="step === 0"
border
stripe
header-align="center"
:data="tableData"
:sort-config="{
// showIcon: false,
trigger: 'cell',
}"
:filter-config="{
showIcon: false,
}"
:column-drag-config="{
showIcon: false,
trigger: 'cell',
}"
:row-config="{
keyField: 'col_FieldName',
useKey: true,
isCurrent: true,
isHover: true,
}"
size="small"
:cell-config="{
height: 26,
}"
:column-config="{
resizable: true,
}"
show-overflow
show-header-overflow
height="100%"
>
<vxe-column field="seq" type="seq" width="50" align="center" title=" " fixed="left" />
<vxe-column title="列名" field="col_FieldName" sortable />
<vxe-column title="标头" field="col_Caption" sortable />
<vxe-column title="宽度" field="col_Width" sortable />
<vxe-column title="导出" field="col_Visible" align="center" width="60" sortable>
<template #default="{ row }">
<el-checkbox v-model="row.col_Visible" true-value="1" false-value="0" />
</template>
</vxe-column>
<vxe-column title="合计" field="" align="center" width="60" sortable>
<template #default="{ row }">
<el-checkbox v-model="row.ShowSummary" true-value="1" false-value="0" disabled />
</template>
</vxe-column>
</vxe-table>
<div v-else class="g3-list-container">
<div class="g3-list-header">
<div>导出 {{ props.total }} 条数据 ,共 {{ totalFiles }} 个文件</div>
<div>
<el-button size="small" type="primary" @click="handleDownloadZip"> 打包导出 </el-button>
</div>
</div>
<div class="g3-list">
<div v-for="(item, index) in files" :key="index" class="g3-list-item">
第 {{ index + 1 }} 份文件({{ item.start }}-{{ item.end }})
<div class="export-button">
<el-button
type="primary"
size="small"
@click="handleExportExcel(item.start, item.end)"
>
导出
</el-button>
</div>
</div>
</div>
</div>
</div>
<template #footer>
<el-button v-if="step === 1" @click="step = 0"> 返回 </el-button>
<el-button
type="primary"
:disabled="loading || tableData.length === 0 || confirmLoading"
@click="handleConfirm"
>
确认
</el-button>
</template>
</el-dialog>
</template>
<style lang="css" scoped>
.g3-container {
height: 500px;
}
.g3-list-container {
display: flex;
flex-direction: column;
}
.g3-list-header {
display: flex;
align-items: center;
justify-content: space-between;
height: 48px;
border-bottom: 1px solid #ddd;
}
.g3-list-container {
height: 100%;
}
.g3-list-header {
font-size: 16px;
font-weight: bold;
color: #333;
}
.g3-list {
display: flex;
flex-direction: column;
overflow-y: auto;
}
.g3-list-item {
background-color: #fff;
padding: 12px 0px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
}
.export-button {
}
</style>