This commit is contained in:
oneao committed 2026-03-24 21:27:27 +08:00
1 parent b7f50f3ccb
commit 11bcef03e1
8 files changed
+437 -537

No files matched your search

+4
View File
@@ -13,6 +13,7 @@ declare module 'vue' {
export interface GlobalComponents {
Directory: typeof import('./src/components/wang-editor/modules/directory.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
@@ -27,9 +28,12 @@ declare module 'vue' {
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSplitter: typeof import('element-plus/es')['ElSplitter']
ElSplitterPanel: typeof import('element-plus/es')['ElSplitterPanel']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
G3Column: typeof import('./src/components/g3-table/g3-column.vue')['default']
@@ -19,3 +19,14 @@
.el-button--large {
height: 32px;
}
.el-loading-mask {
&.is-fullscreen {
.el-loading-spinner .path {
stroke: #fff !important;
}
.el-loading-text {
color: #fff;
}
}
}
+14 -18
View File
@@ -287,7 +287,6 @@ export function getAliyunFileBlob(url) {
// 检查响应状态
if (!response.ok) {
ElMessage.error(`下载失败:${response.status}`);
reject(new Error(`下载失败:${response.status}`));
return;
}
@@ -298,8 +297,6 @@ export function getAliyunFileBlob(url) {
// 返回blob
resolve(blob);
} catch (error) {
// 捕获网络请求失败或其他错误
ElMessage.error("下载文件时发生错误");
reject(error);
}
}
@@ -311,27 +308,26 @@ export function getAliyunFileBlob(url) {
export function downloadZip(files, fileName = "文件合集", callback) {
const zip = new JSZip();
const addedFiles = new Set(); // 用来存储已添加的文件名
const addedFiles = new Set();
let result = files.map((item) => {
let promise = getAliyunFileBlob(item.mx_mapfilename).then((res) => {
// 处理文件名重复的情况
let uniqueFileName = getUniqueFileName(item.mx_filename, addedFiles);
const tasks = files.map((item) => {
return getAliyunFileBlob(item.mx_mapfilename).then((res) => {
const uniqueFileName = getUniqueFileName(item.mx_filename, addedFiles);
zip.file(uniqueFileName, res, { binary: true });
addedFiles.add(uniqueFileName); // 添加文件名到已添加的集合中
addedFiles.add(uniqueFileName);
});
return promise;
});
Promise.all(result)
.then(() => {
zip.generateAsync({ type: "blob" }).then((res) => {
callback && callback();
saveAs(res, `${fileName}.zip`);
});
})
.catch(() => {
// ✅ 关键:return
return Promise.all(tasks)
.then(() => zip.generateAsync({ type: "blob" }))
.then((res) => {
saveAs(res, `${fileName}.zip`);
callback && callback();
})
.catch((err) => {
callback && callback();
throw err; // ✅ 继续往外抛
});
}
@@ -8,7 +8,11 @@ import {
} from "@/request/api";
import { getAlignByFormat, formatValueByFormat } from "@/utils/columns";
import { updateViewTableColumns } from "@/utils/columns";
import { ElMessage } from "element-plus";
import { downloadZip } from "@/utils/file";
import { formatToDate } from "@/utils/time";
import { ElLoading, ElMessage } from "element-plus";
import { saveAs } from "file-saver";
import JSZip from "jszip";
const tableRef = ref();
@@ -49,7 +53,9 @@ const viewName = "v_container";
const showPreview = ref(false);
const previewIndex = ref(-1);
const previewImageList = computed(() => {
return tableData.value.map((item) => item.b_container_img).filter((img) => img);
return tableData.value
.map((item) => item.b_container_img)
.filter((img) => img);
});
const currentRow = ref({});
@@ -172,38 +178,53 @@ async function deleteRows(rows = []) {
return;
}
// 确保 deleteIds 数组中的值是数字,防止 SQL 注入
const safeDeleteIds = deleteIds.map((id) => Number(id));
// 使用安全的 SQL 查询
const fileRes = await loadDataApi("bf_file", `father IN (${safeDeleteIds.join(",")})`, "", [
"subid",
]);
if (!fileRes.success) {
return;
}
for (const file of fileRes.data) {
await deleteFileApi(file.subid);
}
const requestData = [
{
tableName: "b_container",
keyField: "b_id",
inserts: [],
updates: [],
deletes: deleteIds.map((id) => ({ b_id: id })),
},
];
await saveObjectApi(requestData).then((res) => {
if (res.success) {
ElMessage.success("删除成功");
loadTableDatas();
}
const loading = ElLoading.service({
lock: true,
text: "正在删除中...",
});
try {
// 确保 deleteIds 数组中的值是数字,防止 SQL 注入
const safeDeleteIds = deleteIds.map((id) => Number(id));
// 使用安全的 SQL 查询
const fileRes = await loadDataApi(
"bf_file",
`father IN (${safeDeleteIds.join(",")})`,
"",
["subid"],
);
if (!fileRes.success) {
return;
}
for (const file of fileRes.data) {
await deleteFileApi(file.subid);
}
const requestData = [
{
tableName: "b_container",
keyField: "b_id",
inserts: [],
updates: [],
deletes: deleteIds.map((id) => ({ b_id: id })),
},
];
await saveObjectApi(requestData).then((res) => {
if (res.success) {
ElMessage.success("删除成功");
loadTableDatas();
}
});
} catch (err) {
ElMessage.error("删除失败");
console.log(err);
} finally {
loading.close();
}
}
function handleSearch() {
@@ -241,9 +262,167 @@ function handleFileShow(data) {
showFile.value = true;
}
function handleDownloadOne(data) {
console.log(data);
// 获取
async function handleDownloadOne(data) {
const loading = ElLoading.service({
lock: true,
text: "正在下载并打包文件,请稍候...",
});
try {
const fileRes = await loadDataApi(
"bf_file",
`father = '${data.b_id}'`,
"mx_xh asc",
);
if (!fileRes.success) {
ElMessage.error("获取附件失败");
return;
}
const downloadFiles = fileRes.data.map((item, index) => ({
mx_filename: buildFileName(
data.b_container_no,
index + 1,
item.mx_filename,
),
mx_mapfilename: item.mx_mapfilename,
}));
await downloadZip(
downloadFiles,
`${data.b_container_no}_${formatToDate(data.b_inputdatetime)}`,
);
ElMessage.success("下载完成");
} catch (e) {
console.log(e);
ElMessage.error("下载失败");
} finally {
loading.close();
}
}
async function runWithLimit(tasks, limit = 3) {
const results = [];
const executing = [];
for (const task of tasks) {
const p = task().then((res) => {
executing.splice(executing.indexOf(p), 1);
return res;
});
results.push(p);
executing.push(p);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
async function handleDownloadBatch() {
const $table = tableRef.value;
if (!$table) return;
const records = $table.getCheckboxRecords();
if (records.length === 0) {
ElMessage.warning("请选择需要下载的数据");
return;
}
const loading = ElLoading.service({
lock: true,
text: "正在下载并打包文件,请稍候...",
});
const zip = new JSZip();
try {
let total = 0;
let finished = 0;
// 👉 先统计总文件数(用于进度)
for (const item of records) {
const fileRes = await loadDataApi(
"bf_file",
`father = '${item.b_id}'`,
"",
);
if (fileRes.success) {
total += (fileRes.data || []).length;
}
}
// 👉 正式处理
for (const item of records) {
const folderName = `${item.b_container_no}_${formatToDate(
item.b_inputdatetime,
)}`;
const folder = zip.folder(folderName);
const fileRes = await loadDataApi(
"bf_file",
`father = '${item.b_id}'`,
"mx_xh asc",
);
if (!fileRes.success) {
ElMessage.error("获取附件失败");
continue;
}
const files = fileRes.data || [];
const tasks = files.map((file, index) => async () => {
const fileUrl = file.mx_mapfilename;
try {
const res = await fetch(fileUrl);
const blob = await res.blob();
const fileName =
buildFileName(item.b_container_no, index + 1, file.mx_filename) ||
"未命名文件";
folder.file(fileName, blob);
} catch (e) {
ElMessage.error(item.b_container_no + "箱号部分图片下载失败");
console.error("下载失败:", fileUrl);
} finally {
finished++;
loading.setText(`正在下载 ${finished}/${total} 个文件...`);
}
});
// 控制并发数
await runWithLimit(tasks, 3);
}
// 👉 生成 zip
loading.setText("正在生成压缩包...");
const content = await zip.generateAsync({ type: "blob" });
saveAs(content, "批量下载.zip");
ElMessage.success("下载完成");
} catch (error) {
console.error(error);
ElMessage.error("打包失败");
} finally {
loading.close();
}
}
function buildFileName(containerNo, index, fileName) {
const formattedIndex = String(index).padStart(2, "0");
return `${containerNo}_${formattedIndex}.${fileName.split(".").pop()}`;
}
</script>
@@ -299,7 +478,7 @@ function handleDownloadOne(data) {
<div class="g3-content">
<div class="content-top">
<div>
<el-button type="primary">
<el-button type="primary" @click="handleDownloadBatch">
<el-icon class="el-icon--left">
<G3Icon icon="lucide:cloud-download" />
</el-icon>
@@ -368,7 +547,14 @@ function handleDownloadOne(data) {
}"
height="100%"
>
<vxe-column field="seq" type="seq" width="50" align="center" title=" " fixed="left" />
<vxe-column
field="seq"
type="seq"
width="50"
align="center"
title=" "
fixed="left"
/>
<vxe-column type="checkbox" width="50" align="center" fixed="left" />
<vxe-column
v-for="column in columns"
@@ -389,7 +575,12 @@ function handleDownloadOne(data) {
/>
</template>
<template v-else>
{{ formatValueByFormat(row[column.col_FieldName], column.col_DisplayFormat) }}
{{
formatValueByFormat(
row[column.col_FieldName],
column.col_DisplayFormat,
)
}}
</template>
</template>
</vxe-column>
+23 -42
View File
@@ -1,49 +1,30 @@
<script setup lang="js">
const value = ref("");
import { saveAs } from "file-saver";
import JSZip from "jszip";
const options = [
{
value: "Option1",
label: "Option1",
},
{
value: "Option2",
label: "Option2",
},
{
value: "Option3",
label: "Option3",
},
{
value: "Option4",
label: "Option4",
},
{
value: "Option5",
label: "Option5",
},
// 假设这些是文件数据和它们的路径
const files = [
{ name: "file1.txt", folder: "folder1", content: "This is file 1 content" },
{ name: "file2.txt", folder: "folder2", content: "This is file 2 content" },
{ name: "file3.txt", folder: "folder1", content: "This is file 3 content" },
{ name: "file4.txt", folder: "folder3", content: "This is file 4 content" },
];
async function createZip() {
const zip = new JSZip();
// 遍历文件列表,将文件按文件夹添加到 ZIP 文件中
files.forEach((file) => {
// 创建文件夹并将文件添加到对应的文件夹下
zip.folder(file.folder).file(file.name, file.content);
});
// 生成并下载 ZIP 文件
const content = await zip.generateAsync({ type: "blob" });
saveAs(content, "files.zip");
}
</script>
<template>
<G3Grid direction="row">
<G3GridGroup :label-width="80" :width="440" :column="2">
<G3GridItem label="1222222222222222222222222" content="2" :span="1" required>
<el-input type="textarea" v-model="value" />
</G3GridItem>
<G3GridItem label="3" :span="1">
<el-input />
</G3GridItem>
</G3GridGroup>
<G3GridGroup :label-width="80" :width="440" :column="2">
<G3GridItem label="1222222222222222222222222" content="2" :span="1" required>
<el-input type="textarea" v-model="value" />
</G3GridItem>
<G3GridItem label="3" :span="1">
<el-input />
</G3GridItem>
</G3GridGroup>
</G3Grid>
<el-button @click="createZip">测试</el-button>
</template>