u
This commit is contained in:
1 parent
c7240ea7c9
commit
189a085353
26 files changed
+2650
-201
No files matched your search
@@ -47,7 +47,6 @@ export function updateViewTableColumns(tableRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const newColumns = columns
|
||||
.filter((item) => item.type === undefined && item.params && item.params.b_id)
|
||||
.map((item, index) => ({
|
||||
@@ -119,3 +118,35 @@ export function formatValueByFormat(value, format) {
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 format 格式化值
|
||||
* @param {any} value 原始值
|
||||
* @param {string} format 格式描述,如 money(2)、percent(1)
|
||||
*/
|
||||
export function formatValueByFullFormat(value, format) {
|
||||
if (value === null || value === undefined || value === "") return "";
|
||||
|
||||
if (!format || typeof format !== "string") return value;
|
||||
|
||||
const fmt = format.trim().toLowerCase();
|
||||
|
||||
// money(n)
|
||||
if (fmt.startsWith("money")) {
|
||||
const match = fmt.match(/^money\((\d+)\)$/);
|
||||
const decimals = match ? Number(match[1]) : 2;
|
||||
|
||||
const num = Number.parseFloat(value);
|
||||
if (Number.isNaN(num)) return value;
|
||||
|
||||
return num.toFixed(decimals);
|
||||
} else if (fmt === "date") {
|
||||
return formatToDate(value);
|
||||
} else if (fmt === "datetime") {
|
||||
return formatToDateTime(value);
|
||||
} else if (fmt === "number") {
|
||||
return Number(value) || 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
import ExcelJS from "exceljs";
|
||||
import { formatValueByFullFormat, getAlignByFormat } from "@/utils/columns";
|
||||
import { ElLoading, ElMessage } from "element-plus";
|
||||
import { getAliyunFileBlob } from "./file";
|
||||
import { saveAs } from "file-saver";
|
||||
|
||||
export async function ExportVxeTableExcel(tableRef) {
|
||||
const $table = unref(tableRef);
|
||||
|
||||
if (!$table) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: "正在下载Excel文件中,请稍候...",
|
||||
});
|
||||
|
||||
// columns
|
||||
const fullColumns = $table.getFullColumns();
|
||||
const exportColumns = buildColumns(fullColumns) || [];
|
||||
const exportDatas =
|
||||
$table.getCheckboxRecords().length > 0 ? $table.getCheckboxRecords() : $table.getFullData();
|
||||
|
||||
// const footerDatas = updateTableFooterData(exportColumns, exportDatas)
|
||||
const footerDatas = [];
|
||||
|
||||
const options = {
|
||||
columns: exportColumns,
|
||||
data: exportDatas,
|
||||
};
|
||||
|
||||
try {
|
||||
await exportExcel(options);
|
||||
} catch (e) {
|
||||
} finally {
|
||||
loading.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function urlToBase64(url) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const blob = await getAliyunFileBlob(url);
|
||||
|
||||
// Create an object URL from the blob
|
||||
const objectURL = URL.createObjectURL(blob);
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
const img = new Image();
|
||||
|
||||
img.crossOrigin = "Anonymous";
|
||||
img.onload = function () {
|
||||
canvas.height = img.height;
|
||||
canvas.width = img.width;
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||
var dataURL = canvas.toDataURL("image/png");
|
||||
resolve(dataURL);
|
||||
|
||||
// Optional: Revoke the object URL once done to free memory
|
||||
URL.revokeObjectURL(objectURL);
|
||||
};
|
||||
|
||||
img.src = objectURL;
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function buildColumns(arr) {
|
||||
return arr
|
||||
.map((item) => {
|
||||
// 先处理 children(关键!)
|
||||
let children = [];
|
||||
if (item.children && item.children.length > 0) {
|
||||
children = buildColumns(item.children);
|
||||
}
|
||||
|
||||
// 判断当前节点是否需要保留
|
||||
const valid = item.type === undefined && item.field !== undefined;
|
||||
|
||||
// 如果当前不合法 + 没有子节点,直接丢
|
||||
if (!valid && children.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 构建 column
|
||||
const column = {
|
||||
label: item.title,
|
||||
width: item.params?.col_Width || item.renderWidth,
|
||||
field: item.field,
|
||||
type: item.params?.col_DisplayFormat,
|
||||
};
|
||||
|
||||
// 有子节点就挂上
|
||||
if (children.length > 0) {
|
||||
column.children = children;
|
||||
}
|
||||
|
||||
return column;
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出Excel文件(支持多级表头、表尾、图片)
|
||||
* @param {Object} options 导出配置
|
||||
* @param {String} fileName 文件名(默认:export.xlsx)
|
||||
*/
|
||||
export async function exportExcel(options, fileName = "export.xlsx") {
|
||||
const workbook = new ExcelJS.Workbook();
|
||||
const worksheet = workbook.addWorksheet("Sheet1");
|
||||
|
||||
// 1. 处理多级表头
|
||||
const { headerRows, leafColumns, mergeCells } = buildMultiLevelHeader(options.columns);
|
||||
|
||||
// 写入表头行
|
||||
headerRows.forEach((row, rowIndex) => {
|
||||
const headerRow = worksheet.addRow(row);
|
||||
|
||||
// 设置表头样式(突出显示)
|
||||
headerRow.eachCell((cell) => {
|
||||
cell.style = {
|
||||
font: { bold: true, size: 12, color: { argb: "FFFFFFFF" } },
|
||||
fill: {
|
||||
type: "pattern",
|
||||
pattern: "solid",
|
||||
fgColor: { argb: "FF4F81BD" }, // 蓝色背景
|
||||
},
|
||||
border: {
|
||||
top: { style: "thin", color: { argb: "FF000000" } },
|
||||
left: { style: "thin", color: { argb: "FF000000" } },
|
||||
bottom: { style: "thin", color: { argb: "FF000000" } },
|
||||
right: { style: "thin", color: { argb: "FF000000" } },
|
||||
},
|
||||
alignment: { vertical: "middle", horizontal: "center" },
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
// 合并表头单元格
|
||||
mergeCells.forEach((merge) => {
|
||||
worksheet.mergeCells(merge.start.row, merge.start.col, merge.end.row, merge.end.col);
|
||||
});
|
||||
|
||||
// 2. 写入数据行
|
||||
const dataStartRow = headerRows.length + 1;
|
||||
|
||||
for (const [rowIndex, item] of options.data.entries()) {
|
||||
const dataRow = worksheet.addRow([]);
|
||||
|
||||
for (const [colIndex, col] of leafColumns.entries()) {
|
||||
const cell = dataRow.getCell(colIndex + 1);
|
||||
const value = getNestedValue(item, col.field);
|
||||
|
||||
// 处理图片类型
|
||||
if (col.type === "image" && value) {
|
||||
try {
|
||||
|
||||
const base64 = await urlToBase64(value)
|
||||
// 添加图片到工作簿
|
||||
const imageId = workbook.addImage({
|
||||
base64,
|
||||
extension: "png",
|
||||
});
|
||||
|
||||
// 计算图片位置(跳过表头行)
|
||||
const imageRow = dataStartRow + rowIndex;
|
||||
worksheet.addImage(imageId, {
|
||||
tl: { col: colIndex, row: imageRow - 1 },
|
||||
ext: { width: col.imageWidth || 80, height: col.imageHeight || 80 },
|
||||
});
|
||||
|
||||
// 调整行高以适应图片
|
||||
dataRow.height = Math.max(dataRow.height || 20, (col.imageHeight || 80) * 0.75);
|
||||
} catch (error) {
|
||||
console.warn(`图片导出失败: ${error.message}`);
|
||||
cell.value = "[图片]";
|
||||
}
|
||||
} else {
|
||||
cell.value = value;
|
||||
}
|
||||
|
||||
// 设置数据行样式
|
||||
cell.style = {
|
||||
alignment: { vertical: "middle", horizontal: "left" },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 写入表尾数据(突出显示)
|
||||
if (options.footer && options.footer.length > 0) {
|
||||
const footerStartRow = dataStartRow + options.data.length;
|
||||
|
||||
options.footer.forEach((footerItem, rowIndex) => {
|
||||
const footerRow = worksheet.addRow([]);
|
||||
|
||||
leafColumns.forEach((col, colIndex) => {
|
||||
const cell = footerRow.getCell(colIndex + 1);
|
||||
const value = getNestedValue(footerItem, col.field);
|
||||
cell.value = value;
|
||||
|
||||
// 设置表尾样式(突出显示)
|
||||
cell.style = {
|
||||
font: { bold: true },
|
||||
// fill: {
|
||||
// type: "pattern",
|
||||
// pattern: "solid",
|
||||
// // fgColor: { argb: "FFC00000" }, // 红色背景
|
||||
// },
|
||||
// border: {
|
||||
// top: { style: "medium", color: { argb: "FF000000" } },
|
||||
// left: { style: "medium", color: { argb: "FF000000" } },
|
||||
// bottom: { style: "medium", color: { argb: "FF000000" } },
|
||||
// right: { style: "medium", color: { argb: "FF000000" } },
|
||||
// },
|
||||
alignment: { vertical: "middle", horizontal: "center" },
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 4. 设置列宽
|
||||
leafColumns.forEach((col, index) => {
|
||||
const column = worksheet.getColumn(index + 1);
|
||||
column.width = col.width ? col.width / 7 : 20; // 转换像素为Excel单位
|
||||
});
|
||||
|
||||
// 5. 生成并保存文件
|
||||
const buffer = await workbook.xlsx.writeBuffer();
|
||||
const blob = new Blob([buffer], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
});
|
||||
saveAs(blob, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建多级表头
|
||||
* @param {Array} columns 表头配置
|
||||
* @returns {Object} { headerRows, leafColumns, mergeCells }
|
||||
*/
|
||||
function buildMultiLevelHeader(columns) {
|
||||
// 获取表头最大深度
|
||||
const maxDepth = getMaxDepth(columns);
|
||||
|
||||
// 获取所有叶子节点列
|
||||
const leafColumns = getLeafColumns(columns);
|
||||
|
||||
// 初始化表头二维数组
|
||||
const headerRows = Array.from({ length: maxDepth }, () => Array(leafColumns.length).fill(""));
|
||||
|
||||
const mergeCells = [];
|
||||
|
||||
// 递归填充表头
|
||||
function fillHeader(nodes, startCol = 0, depth = 0) {
|
||||
let currentCol = startCol;
|
||||
|
||||
nodes.forEach((node) => {
|
||||
// 计算当前节点跨越的列数
|
||||
const colSpan = node.children ? getLeafCount(node.children) : 1;
|
||||
|
||||
// 填充当前节点的表头
|
||||
headerRows[depth][currentCol] = node.label || "";
|
||||
|
||||
// 如果有子节点,递归处理
|
||||
if (node.children && node.children.length > 0) {
|
||||
fillHeader(node.children, currentCol, depth + 1);
|
||||
|
||||
// 如果跨多列,需要合并单元格
|
||||
if (colSpan > 1) {
|
||||
mergeCells.push({
|
||||
start: { row: depth + 1, col: currentCol + 1 },
|
||||
end: { row: depth + 1, col: currentCol + colSpan },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 如果当前节点有父节点且不是叶子节点,需要纵向合并
|
||||
if (depth < maxDepth - 1 && !node.children) {
|
||||
mergeCells.push({
|
||||
start: { row: depth + 1, col: currentCol + 1 },
|
||||
end: { row: maxDepth, col: currentCol + 1 },
|
||||
});
|
||||
}
|
||||
|
||||
currentCol += colSpan;
|
||||
});
|
||||
}
|
||||
|
||||
fillHeader(columns);
|
||||
|
||||
return { headerRows, leafColumns, mergeCells };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取树的最大深度
|
||||
*/
|
||||
function getMaxDepth(nodes, currentDepth = 1) {
|
||||
if (!nodes || nodes.length === 0) return currentDepth;
|
||||
|
||||
let maxDepth = currentDepth;
|
||||
nodes.forEach((node) => {
|
||||
if (node.children && node.children.length > 0) {
|
||||
const depth = getMaxDepth(node.children, currentDepth + 1);
|
||||
maxDepth = Math.max(maxDepth, depth);
|
||||
}
|
||||
});
|
||||
|
||||
return maxDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有叶子节点列
|
||||
*/
|
||||
function getLeafColumns(nodes, result = []) {
|
||||
nodes.forEach((node) => {
|
||||
if (node.children && node.children.length > 0) {
|
||||
getLeafColumns(node.children, result);
|
||||
} else {
|
||||
result.push({
|
||||
field: node.field,
|
||||
label: node.label,
|
||||
width: node.width,
|
||||
type: node.type,
|
||||
imageWidth: node.imageWidth,
|
||||
imageHeight: node.imageHeight,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取叶子节点数量
|
||||
*/
|
||||
function getLeafCount(nodes) {
|
||||
if (!nodes || nodes.length === 0) return 0;
|
||||
|
||||
return nodes.reduce((count, node) => {
|
||||
if (node.children && node.children.length > 0) {
|
||||
return count + getLeafCount(node.children);
|
||||
}
|
||||
return count + 1;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取嵌套对象的值
|
||||
*/
|
||||
function getNestedValue(obj, path) {
|
||||
if (!path) return "";
|
||||
|
||||
return (
|
||||
path.split(".").reduce((prev, curr) => {
|
||||
return prev ? prev[curr] : "";
|
||||
}, obj) || ""
|
||||
);
|
||||
}
|
||||
@@ -203,14 +203,113 @@ export function getFileExtension(fileName) {
|
||||
return fileExt;
|
||||
}
|
||||
|
||||
// 上传到阿里云
|
||||
// blob fileName fileSize xh moduleid father cateid
|
||||
export function downloadZip(files, fileName = "文件合集", callback) {
|
||||
const zip = new JSZip();
|
||||
const addedFiles = new Set();
|
||||
|
||||
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);
|
||||
})
|
||||
.catch((err) => {
|
||||
// 捕获单个文件下载失败的错误,不影响其他文件下载
|
||||
console.error(`Failed to download file: ${item.mx_filename}`, err);
|
||||
});
|
||||
});
|
||||
|
||||
// 关键:使用 Promise.all 确保所有任务完成
|
||||
return Promise.all(tasks)
|
||||
.then(() => zip.generateAsync({ type: "blob" }))
|
||||
.then((res) => {
|
||||
saveAs(res, `${fileName}.zip`);
|
||||
callback && callback();
|
||||
})
|
||||
.catch((err) => {
|
||||
callback && callback();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
// 处理文件名重复的函数
|
||||
function getUniqueFileName(fileName, addedFiles) {
|
||||
let uniqueFileName = fileName;
|
||||
let counter = 1;
|
||||
|
||||
// 如果文件名已经存在,则在文件名后加上 (1), (2), ...
|
||||
while (addedFiles.has(uniqueFileName)) {
|
||||
const fileNameWithoutExtension = uniqueFileName.replace(/\.[^/.]+$/, "");
|
||||
const extension = uniqueFileName.slice(uniqueFileName.lastIndexOf("."));
|
||||
uniqueFileName = `${fileNameWithoutExtension} (${counter})${extension}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
return uniqueFileName;
|
||||
}
|
||||
|
||||
// ============================================ 阿里云上传和下载 ============================================
|
||||
|
||||
let cachedCredentials = null; // 用来缓存凭证
|
||||
let isFetchingCredentials = false; // 控制是否正在请求凭证
|
||||
|
||||
// 获取阿里云文件的上传凭证
|
||||
export function getAliyunUploadCredentials() {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 如果缓存中有有效的凭证,则直接使用
|
||||
if (cachedCredentials && !isCredentialsExpired(cachedCredentials)) {
|
||||
resolve(cachedCredentials); // 凭证未过期,直接返回缓存的凭证
|
||||
} else {
|
||||
// 如果正在请求凭证,则等待
|
||||
if (isFetchingCredentials) {
|
||||
const interval = setInterval(() => {
|
||||
// 等待直到凭证被请求完毕
|
||||
if (!isFetchingCredentials) {
|
||||
clearInterval(interval); // 请求完成后停止轮询
|
||||
resolve(cachedCredentials); // 返回缓存的凭证
|
||||
}
|
||||
}, 100); // 每100ms检查一次
|
||||
} else {
|
||||
// 如果凭证过期,重新请求新的凭证
|
||||
isFetchingCredentials = true; // 设置为正在请求凭证状态
|
||||
getAliyunKeyApi()
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
const { accessid, policy, signature, expire, dir, host } = res.data;
|
||||
|
||||
// 缓存凭证,并记录过期时间
|
||||
cachedCredentials = {
|
||||
accessid,
|
||||
policy,
|
||||
signature,
|
||||
expire: parseInt(expire, 10),
|
||||
dir,
|
||||
host,
|
||||
};
|
||||
|
||||
isFetchingCredentials = false; // 请求完成,解除锁定
|
||||
resolve(cachedCredentials); // 返回新凭证
|
||||
} else {
|
||||
isFetchingCredentials = false; // 请求失败,解除锁定
|
||||
reject(new Error("获取凭证失败"));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
isFetchingCredentials = false; // 请求失败,解除锁定
|
||||
reject(new Error("请求凭证失败:" + err.message));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 上传文件到阿里云
|
||||
export function uploadFile(blob, fileName, fileExt, fileSize, xh, moduleid, father, cateid) {
|
||||
return new Promise((resolve, reject) => {
|
||||
getAliyunKeyApi()
|
||||
.then(async (res) => {
|
||||
getAliyunUploadCredentials()
|
||||
.then(async (aliyunKey) => {
|
||||
try {
|
||||
const aliyunKey = res.data;
|
||||
const formData = new FormData();
|
||||
|
||||
let uniqueName = `${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
||||
@@ -219,12 +318,17 @@ export function uploadFile(blob, fileName, fileExt, fileSize, xh, moduleid, fath
|
||||
uniqueName += `.${fileExt}`;
|
||||
}
|
||||
|
||||
// 构造上传的文件路径
|
||||
formData.append("key", `${aliyunKey.dir}${uniqueName}`);
|
||||
formData.append("OSSAccessKeyId", aliyunKey.accessid);
|
||||
formData.append("policy", aliyunKey.policy);
|
||||
formData.append("Signature", aliyunKey.signature);
|
||||
formData.append("file", blob, uniqueName);
|
||||
|
||||
console.log(formData);
|
||||
console.log(aliyunKey.host);
|
||||
console.log(aliyunKey.dir);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", aliyunKey.host, true);
|
||||
|
||||
@@ -232,6 +336,7 @@ export function uploadFile(blob, fileName, fileExt, fileSize, xh, moduleid, fath
|
||||
if (xhr.status === 200 || xhr.status === 204) {
|
||||
const allFilePath = `${aliyunKey.host}/${aliyunKey.dir}${uniqueName}`;
|
||||
|
||||
// 上传后处理(插入文件记录等)
|
||||
const insertRes = await insertFileApi({
|
||||
father,
|
||||
mx_moduleid: moduleid,
|
||||
@@ -278,76 +383,35 @@ export function uploadFile(blob, fileName, fileExt, fileSize, xh, moduleid, fath
|
||||
});
|
||||
}
|
||||
|
||||
// 检查凭证是否过期
|
||||
function isCredentialsExpired(credentials) {
|
||||
const currentTime = Math.floor(Date.now() / 1000);
|
||||
return currentTime > credentials.expire;
|
||||
}
|
||||
|
||||
// 获取阿里云文件的 Blob 数据(用于下载)
|
||||
export function getAliyunFileBlob(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 创建一个独立的异步函数来处理 fetch 请求
|
||||
async function fetchFile() {
|
||||
try {
|
||||
const response = await fetch(url, { mode: "cors" });
|
||||
getAliyunUploadCredentials()
|
||||
.then((aliyunKey) => {
|
||||
const downloadUrl = `${url}?OSSAccessKeyId=${aliyunKey.accessid}&policy=${aliyunKey.policy}&Signature=${aliyunKey.signature}`;
|
||||
|
||||
// 检查响应状态
|
||||
if (!response.ok) {
|
||||
reject(new Error(`下载失败:${response.status}`));
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取文件内容
|
||||
const blob = await response.blob();
|
||||
|
||||
// 返回blob
|
||||
resolve(blob);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
// 调用异步函数
|
||||
fetchFile();
|
||||
});
|
||||
}
|
||||
|
||||
export function downloadZip(files, fileName = "文件合集", callback) {
|
||||
const zip = new JSZip();
|
||||
const addedFiles = new Set();
|
||||
|
||||
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);
|
||||
fetch(downloadUrl, { mode: "cors" })
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`下载失败,状态码:${response.status}`);
|
||||
}
|
||||
return response.blob();
|
||||
})
|
||||
.then((blob) => {
|
||||
resolve(blob);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
// 捕获单个文件下载失败的错误,不影响其他文件下载
|
||||
console.error(`Failed to download file: ${item.mx_filename}`, err);
|
||||
});
|
||||
.catch(reject);
|
||||
});
|
||||
|
||||
// 关键:使用 Promise.all 确保所有任务完成
|
||||
return Promise.all(tasks)
|
||||
.then(() => zip.generateAsync({ type: "blob" }))
|
||||
.then((res) => {
|
||||
saveAs(res, `${fileName}.zip`);
|
||||
callback && callback();
|
||||
})
|
||||
.catch((err) => {
|
||||
callback && callback();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
// 处理文件名重复的函数
|
||||
function getUniqueFileName(fileName, addedFiles) {
|
||||
let uniqueFileName = fileName;
|
||||
let counter = 1;
|
||||
|
||||
// 如果文件名已经存在,则在文件名后加上 (1), (2), ...
|
||||
while (addedFiles.has(uniqueFileName)) {
|
||||
const fileNameWithoutExtension = uniqueFileName.replace(/\.[^/.]+$/, "");
|
||||
const extension = uniqueFileName.slice(uniqueFileName.lastIndexOf("."));
|
||||
uniqueFileName = `${fileNameWithoutExtension} (${counter})${extension}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
return uniqueFileName;
|
||||
}
|
||||
// ============================================ 阿里云上传和下载 ============================================
|
||||
Reference in new issue
Block a user