效果:
需求:
- 使用el-upload支持用户上传图片,粘贴图片
- Proof这里是上传的证明材料;原先设计的是根据不同的type,支持用户传图片或者输入相关链接
代码:
- 页面
<div class="info-item" style="align-items: start">
<span class="label">Proof:</span>
<div class="two-col-container">
<div
class="item"
v-for="(
proofType, index
) in approvalInformationObj.requiredProofTypes"
:key="proofType.id"
>
<div class="proof-item">
<div class="label" style="text-align: left;display: flex;">
<span v-if="proofType.required" style="color: red"
>*</span
>
<span
class="proof-label-text"
:title="proofType.value"
>{{ proofType.value }}:</span
>
</div>
<div class="upload-container">
<!-- 图片上传类型 (type: 0) -->
<template v-if="proofType.type === 0">
<div
:class="`upload-area ${
currentFocusArea === index
? 'active-upload'
: ''
}`"
@click="setActiveUploadArea(index)"
@mouseenter="showUploadHint(index, $event)"
@mouseleave="hideUploadHint()"
>
<el-upload
:class="`image-uploader proof-uploader-${index}`"
:action="uploadAction"
:show-file-list="false"
:on-success="(response: any, file: any) => handleProofSuccess(response, file, index)"
:before-upload="beforeUpload"
:http-request="(options: any) => customUploadProof(options, index)"
:auto-upload="true"
list-type="picture-card"
>
<el-icon title="Supports uploading files"><Plus /></el-icon>
<!-- <img
v-if="proofData[index]?.displayUrl"
:src="proofData[index].displayUrl"
class="upload-image"
/>
<div v-else class="upload-placeholder">
<i class="el-icon-plus"></i>
<div>Upload pictures</div>
</div> -->
</el-upload>
<!-- 当前活跃上传区域指示器 -->
<!-- <div
v-if="currentFocusArea === index"
class="active-indicator"
>
<span>当前选中</span>
</div> -->
</div>
<!-- 显示已上传的多个图片 -->
<div
v-if="proofData[index]?.images?.length > 0"
class="uploaded-images"
>
<div
v-for="(image, imageIndex) in proofData[index]
.images"
:key="imageIndex"
class="image-item"
:class="{
active:
image.url === proofData[index].displayUrl,
}"
>
<img
:src="image.url"
class="thumbnail"
@click="
changeDisplayImage(index, image.url)
"
/>
<div class="image-actions">
<el-button
type="primary"
:icon="Search"
circle
@click.stop="previewImage(image.url)"
title="preview"
/>
<el-button
type="danger"
:icon="Delete"
circle
@click.stop="
removeImage(index, imageIndex)
"
title="delete"
/>
</div>
</div>
</div>
</template>
<!-- 链接输入类型 (type: 1) -->
<template v-else-if="proofType.type === 1">
<el-input
v-model="proofData[index].url"
type="textarea"
:rows="3"
:placeholder="`Please input ${proofType.value} URL or text content`"
style="margin-top: 10px"
/>
</template>
</div>
<!-- 描述信息 -->
<!-- <div
v-if="proofType.description"
class="proof-description"
>
{{ proofType.description }}
</div> -->
</div>
</div>
</div>
</div>
- 方法,相关方法自行搜索吧,太多了,懒得拆分了,记录下
<script lang="ts" setup>
import { formatAmount } from "@/utils/format";
import {
CommissionType,
CommissionTypeEnum,
COMMISSION_TYPE_MAP,
} from "@/types/performance";
import { removeFormulaPrefix } from "@/utils/format";
const pageId = ref(0);
interface PersonWorkload {
id: number;
name: string;
role: string;
proportionNum: string;
}
interface QuarterItem {
name: string;
value: {
year: number;
beginTime: string;
endTime: string;
quarter: number;
};
}
// 表单数据
const commissionType = ref<CommissionType>("");
const formulaBase = ref(36);
const formulaMultiplier = ref(180);
const selectedCategory = ref();
const selectedQuarter = ref();
// Other类型相关变量
const selectedJournal = ref();
const selectedRole = ref();
const proportionNum = ref();
const commissionTypeEnumList = ref<CommissionTypeEnum[]>([]);
const categoryList = ref([]);
// 计算当前选中category的label值,用于title属性
const selectedCategoryLabel = computed(() => {
if (!selectedCategory.value || !categoryList.value.length) {
return "";
}
const selectedItem = categoryList.value.find(
(item: any) => item.id === selectedCategory.value
);
return selectedItem ? selectedItem.value : "";
});
let personalInformationObj = reactive<any>({
email: "",
chineseName: "",
role: "",
journals: [],
time: "",
qcPassRate: "",
paidConfirmed: "",
pi: null,
createdTime: "",
finalConfirmed: ''
});
interface ProofType {
id: number;
type: number;
value: string;
description: string;
required: boolean;
}
let approvalInformationObj = reactive({
performanceSettingId: "",
commissionTypeId: "",
jmRole: "",
jmPaidConfirmed: "",
jmPassRate: "",
jmProportion: "",
calcalationFormula: "",
paperId: "",
sectionId: "",
remark: "",
amount: "",
siTitle: "",
siPublished: null,
siType: "",
articleType: "",
paymentAmount: "",
requiredProofTypes: [] as ProofType[],
deTotalCommission: null,
});
const showSave = ref(false);
const showSendToApprove = ref(false);
// 图片上传相关
const uploadAction = ref(""); // 实际使用时替换为后端上传接口
// 新的证明材料数据结构
const proofData = ref<{
[key: number]: {
displayUrl?: string;
url?: string;
note?: string;
fileId?: number;
images?: { url: string; name: string; fileId?: number }[];
};
}>({});
// 压缩包上传相关数据
const uploadedFiles = ref<{
name: string;
size: number;
fileId?: number;
originalFile?: File;
}[]>([]);;
// 当前焦点区域
const currentFocusArea = ref<any>(0);
// 是否显示焦点提示
const showFocusHint = ref(false);
// 焦点提示的位置
const focusHintPosition = ref({ top: 0, left: 0 });
// 设置活跃上传区域
const setActiveUploadArea = (index: number) => {
currentFocusArea.value = index;
// 显示简短提示
showFocusHint.value = true;
setTimeout(() => {
showFocusHint.value = false;
}, 1500);
};
// 显示上传提示
const showUploadHint = (index: number, event: MouseEvent) => {
const rect = (event.currentTarget as HTMLElement).getBoundingClientRect();
focusHintPosition.value = {
top: rect.top - 30,
left: rect.left + rect.width / 2 - 100,
};
// 临时更新焦点区域用于显示提示文本
const tempFocus = currentFocusArea.value;
currentFocusArea.value = index;
showFocusHint.value = true;
// 恢复原来的焦点区域
setTimeout(() => {
if (currentFocusArea.value === index && !showFocusHint.value) {
currentFocusArea.value = tempFocus;
}
}, 100);
};
// 隐藏上传提示
const hideUploadHint = () => {
showFocusHint.value = false;
};
// 显示焦点提示
const showFocusIndicator = (e: MouseEvent, index: number) => {
// 更新当前焦点区域
currentFocusArea.value = index;
// 计算提示位置
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect();
focusHintPosition.value = {
top: rect.top - 25,
left: rect.left + rect.width / 2 - 75,
};
// 显示提示
showFocusHint.value = true;
// 3秒后自动隐藏
setTimeout(() => {
showFocusHint.value = false;
}, 2000);
};
// 更新事件监听器
const updateEventListeners = () => {
// 移除旧的事件监听器,避免重复绑定
document.querySelectorAll(".upload-container").forEach((container) => {
container.removeEventListener("click", () => {});
});
// 添加新的事件监听器
const invitationUploader = document.querySelector(".invitation-uploader");
const scopusUploader = document.querySelector(".scopus-uploader");
const referencesUploader = document.querySelector(".references-uploader");
const identityUploader = document.querySelector(".identity-uploader");
if (invitationUploader) {
invitationUploader.addEventListener("click", (e) => {
currentFocusArea.value = "invitation";
showFocusIndicator(e as MouseEvent, "invitation");
});
}
if (scopusUploader) {
scopusUploader.addEventListener("click", (e) => {
currentFocusArea.value = "scopus";
showFocusIndicator(e as MouseEvent, "scopus");
});
}
if (referencesUploader) {
referencesUploader.addEventListener("click", (e) => {
currentFocusArea.value = "references";
showFocusIndicator(e as MouseEvent, "references");
});
}
if (identityUploader) {
identityUploader.addEventListener("click", (e) => {
currentFocusArea.value = "identity";
showFocusIndicator(e as MouseEvent, "identity");
});
}
// 添加上传区域的容器监听
document.querySelectorAll(".upload-container").forEach((container) => {
// 判断这是哪个上传区域
if (container.querySelector(".invitation-uploader")) {
container.addEventListener("click", (e) => {
if (e.target === container) {
currentFocusArea.value = "invitation";
showFocusIndicator(e as MouseEvent, "invitation");
// 点击容器时,模拟点击上传组件
const uploader = container.querySelector(".invitation-uploader");
if (uploader && uploader instanceof HTMLElement) {
uploader.click();
}
}
});
} else if (container.querySelector(".scopus-uploader")) {
container.addEventListener("click", (e) => {
if (e.target === container) {
currentFocusArea.value = "scopus";
showFocusIndicator(e as MouseEvent, "scopus");
// 点击容器时,模拟点击上传组件
const uploader = container.querySelector(".scopus-uploader");
if (uploader && uploader instanceof HTMLElement) {
uploader.click();
}
}
});
} else if (container.querySelector(".references-uploader")) {
container.addEventListener("click", (e) => {
if (e.target === container) {
currentFocusArea.value = "references";
showFocusIndicator(e as MouseEvent, "references");
// 点击容器时,模拟点击上传组件
const uploader = container.querySelector(".references-uploader");
if (uploader && uploader instanceof HTMLElement) {
uploader.click();
}
}
});
} else if (container.querySelector(".identity-uploader")) {
container.addEventListener("click", (e) => {
if (e.target === container) {
currentFocusArea.value = "identity";
showFocusIndicator(e as MouseEvent, "identity");
// 点击容器时,模拟点击上传组件
const uploader = container.querySelector(".identity-uploader");
if (uploader && uploader instanceof HTMLElement) {
uploader.click();
}
}
});
}
});
};
const quarterList = ref<QuarterItem[]>([]);
// 获取当前选中季度的数据
const currentQuarterData = computed(() => {
return quarterList.value?.find((item) => item.name === selectedQuarter.value);
});
const needTimeTypes = [
CommissionType.DE_JOB_GRADE,
CommissionType.AE_JOB_GRADE,
];
const beginTime = !needTimeTypes.includes(commissionType.value)
? currentQuarterData.value?.value.beginTime
: null;
const endTime = !needTimeTypes.includes(commissionType.value)
? currentQuarterData.value?.value.endTime
: null;
// 存储原始金额,用于按比例计算
const originalAmount = ref<string>("");
// 计算金额的函数
const calculateAmount = () => {
if (proportionNum.value && originalAmount.value) {
const proportion = parseFloat(proportionNum.value) / 100; // 将百分比转换为小数
const original = parseFloat(originalAmount.value);
if (!isNaN(proportion) && !isNaN(original)) {
approvalInformationObj.amount = (original * proportion).toFixed(2);
}
} else if (!proportionNum.value && originalAmount.value) {
// 如果比例为空,恢复原始金额
approvalInformationObj.amount = originalAmount.value;
}
};
// 监听proportionNum变化,重新计算amount
watch(proportionNum, () => {
calculateAmount();
});
const canEditRequestedAmount = ref(false);
const performanceSettingLength = ref();
const sameApplications = ref(false)
// 查询申请信息,及基本信息qc pass rate, paid confirmed, pi等信息
const getApprovalDetailsData = (
categoryId: number | string,
beginTime?: string,
endTime?: string,
needCategoryList: boolean = true,
uncheckedNumber: number = 0
) => {
const needPaperId = [CommissionType.PAPER_EXTRA, CommissionType.OTHER].includes(commissionType.value);
const needSectionId = [CommissionType.SI_EXTRA, CommissionType.OTHER].includes(commissionType.value);
performanceSettingsByCommissionId({
commissionId: commissionType.value,
categoryId: categoryId || 0,
journalId: selectedJournal.value,
beginTime: beginTime || null,
endTime: endTime || null,
requestedId: needPaperId ? approvalInformationObj.paperId : null, // 文章id
sectionId: needSectionId ? approvalInformationObj.sectionId : null,
uncheckedNumber: uncheckedNumber || uncheckedIds.value.length
}).then((res) => {
if (res && res.code === 2000 && res.data) {
// 获取category数据
if (needCategoryList) {
categoryList.value = res.data.performanceCategoryEnumList || [];
}
// 获取基本信息
personalInformationObj.qcPassRate =
res.data.calculateConfigValueMap?.qcPassRate || 0;
personalInformationObj.paidConfirmed =
res.data.calculateConfigValueMap?.paidConfirmedNumber || 0;
personalInformationObj.finalConfirmed = personalInformationObj.paidConfirmed - uncheckedIds.value.length
personalInformationObj.pi =
res.data.calculateConfigValueMap?.piNumber || 0;
// 获取申请信息
approvalInformationObj.calcalationFormula =
res.data.performanceSetting?.[0]?.amount || "";
// 保存原始金额
const requestedAmount =
res.data.performanceSetting?.[0]?.requestedAmount ?? "";
originalAmount.value = requestedAmount;
approvalInformationObj.amount = requestedAmount;
// 如果proportionNum已有值,重新计算金额
setTimeout(() => {
calculateAmount();
}, 0);
approvalInformationObj.performanceSettingId =
res.data.performanceSetting?.[0]?.id || "";
approvalInformationObj.commissionTypeId =
res.data.performanceSetting?.[0]?.commissionTypeId || "";
approvalInformationObj.articleType = res.data.article?.articleType;
approvalInformationObj.paymentAmount = res.data.article?.apc;
approvalInformationObj.requiredProofTypes =
res.data.requiredProofTypes || [];
approvalInformationObj.deTotalCommission =
res.data.calculateConfigValueMap?.deTotalCommission || 0;
canEditRequestedAmount.value = res.data.canEditRequestedAmount;
performanceSettingLength.value = res.data.performanceSetting?.length;
sameApplications.value = res.data.sameApplications?.length > 0
// 初始化 证明材料数据
if (
res.data.requiredProofTypes &&
res.data.requiredProofTypes.length > 0
) {
res.data.requiredProofTypes.forEach((_, index: number) => {
if (!proofData.value[index]) {
proofData.value[index] = {
images: [],
url: "",
displayUrl: "",
note: "",
fileId: undefined,
};
}
});
}
}
});
};
const journalOptions = ref<any[]>([]);
const roleOptions = ref<any[]>([]);
// 监听粘贴事件,实现粘贴上传图片
onMounted(() => {
document.addEventListener("paste", handlePaste);
// 初始化事件监听器
setTimeout(updateEventListeners, 500);
// 添加拖拽区域监听
window.addEventListener("dragover", (e) => {
// 阻止默认行为
e.preventDefault();
});
window.addEventListener("drop", handleDrop);
return () => {
window.removeEventListener("dragover", (e) => e.preventDefault());
};
});
// 处理拖拽事件
const handleDrop = (e: DragEvent) => {
e.preventDefault();
// 检查拖拽的是否为文件
if (e.dataTransfer?.files.length) {
// 根据拖拽位置判断目标区域
const target = document.elementFromPoint(e.clientX, e.clientY);
if (!target) return;
// 查找最近的上传区域
let proofIndex = -1;
let container = null;
// 尝试从点击元素向上查找上传容器
let element = target as HTMLElement;
while (element && proofIndex === -1) {
if (element.classList?.contains("upload-container")) {
container = element;
// 查找proof-uploader的索引
const uploader = element.querySelector("[class*='proof-uploader-']");
if (uploader) {
const classList = Array.from(uploader.classList);
const uploaderClass = classList.find((cls) =>
cls.startsWith("proof-uploader-")
);
if (uploaderClass) {
proofIndex = parseInt(uploaderClass.split("-")[2]);
}
}
break;
}
// 直接检查当前元素是否是上传器
if (element.classList.value.includes("proof-uploader-")) {
const classList = Array.from(element.classList);
const uploaderClass = classList.find((cls) =>
cls.startsWith("proof-uploader-")
);
if (uploaderClass) {
proofIndex = parseInt(uploaderClass.split("-")[2]);
}
break;
}
element = element.parentElement as HTMLElement;
}
if (proofIndex !== -1) {
// 更新当前焦点区域
currentFocusArea.value = proofIndex;
// 处理所有拖拽的文件
Array.from(e.dataTransfer.files).forEach((file) => {
if (file.type.startsWith("image/")) {
handleImageFile(file, proofIndex);
}
});
// 如果找到了容器,显示焦点提示
if (container) {
showFocusIndicator(e as unknown as MouseEvent, proofIndex);
}
}
}
};
// 组件卸载时移除事件监听
onUnmounted(() => {
document.removeEventListener("paste", handlePaste);
window.removeEventListener("dragover", (e) => e.preventDefault());
window.removeEventListener("drop", handleDrop);
});
// 处理粘贴事件
const handlePaste = (event: ClipboardEvent) => {
const items = event.clipboardData?.items;
if (!items) return;
// 检查当前焦点元素,判断应该上传到哪个区域
const activeElement = document.activeElement;
let targetIndex = currentFocusArea.value;
let foundArea = false;
// 根据当前激活的元素判断上传区域
if (activeElement) {
// 尝试从当前元素向上查找上传容器
let element = activeElement as HTMLElement;
while (element && !foundArea) {
if (element.classList?.contains("upload-container")) {
// 查找proof-uploader的索引
const uploader = element.querySelector("[class*='proof-uploader-']");
if (uploader) {
const classList = Array.from(uploader.classList);
const uploaderClass = classList.find((cls) =>
cls.startsWith("proof-uploader-")
);
if (uploaderClass) {
targetIndex = parseInt(uploaderClass.split("-")[2]);
foundArea = true;
}
}
break;
}
// 直接检查当前元素是否是上传器
if (element.classList.value.includes("proof-uploader-")) {
const classList = Array.from(element.classList);
const uploaderClass = classList.find((cls) =>
cls.startsWith("proof-uploader-")
);
if (uploaderClass) {
targetIndex = parseInt(uploaderClass.split("-")[2]);
foundArea = true;
}
break;
}
element = element.parentElement as HTMLElement;
}
}
let imageFound = false;
// 处理所有粘贴的图片
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
const file = items[i].getAsFile();
if (!file) continue;
imageFound = true;
// 使用确定的目标区域上传图片
handleImageFile(file, targetIndex);
}
}
// 只有找到图片时才阻止默认粘贴行为
if (imageFound) {
event.preventDefault();
// 更新当前焦点区域,以便下次粘贴时使用
currentFocusArea.value = targetIndex;
}
};
// 上传前检查
const beforeUpload = (file: File) => {
const isImage = file.type.startsWith("image/");
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isImage) {
ElMessage.error("Only image files can be uploaded!");
return false;
}
if (!isLt5M) {
ElMessage.error("The image size must not exceed 5MB.");
return false;
}
return true;
};
// 处理图片文件
const handleImageFile = async (file: File, index: number) => {
if (!beforeUpload(file)) return;
// 初始化该索引的数据对象
if (!proofData.value[index]) {
proofData.value[index] = { images: [] };
}
if (!proofData.value[index].images) {
proofData.value[index].images = [];
}
try {
// 调用真实上传接口
const uploadResponse = await uploadFileApiNew(file);
if (uploadResponse.code === 2000 && uploadResponse.data) {
const fileId = uploadResponse.data.uploadFileData.id || "";
const imgUrl = URL.createObjectURL(file);
const imageData = {
url: imgUrl,
name: file.name,
fileId: fileId,
};
proofData.value[index].images!.push(imageData);
proofData.value[index].displayUrl = imgUrl;
proofData.value[index].fileId = fileId; // 记录当前显示图片的文件ID
ElMessage.success("mage uploaded successfully.");
} else {
ElMessage.error("Image upload failed.");
}
} catch (error) {
console.error("Upload error:", error);
ElMessage.error("Image upload failed.");
}
};
// 自定义上传函数
const customUploadProof = (options: any, index: number) => {
handleImageFile(options.file, index);
};
// 上传成功回调
const handleProofSuccess = async (response: any, file: any, index: number) => {
// 初始化该索引的数据对象
if (!proofData.value[index]) {
proofData.value[index] = { images: [] };
}
if (!proofData.value[index].images) {
proofData.value[index].images = [];
}
try {
// 调用真实上传接口
const uploadResponse = await uploadFileApiNew(file.raw);
if (uploadResponse.code === 2000 && uploadResponse.data) {
const fileId = uploadResponse.data.id;
const url = URL.createObjectURL(file.raw);
proofData.value[index].images!.push({
url,
name: file.name,
fileId: fileId,
});
proofData.value[index].displayUrl = url;
proofData.value[index].fileId = fileId; // 记录当前显示图片的文件ID
ElMessage.success("Image uploaded successfully.");
} else {
ElMessage.error("Image upload failed.");
}
} catch (error) {
console.error("Upload error:", error);
ElMessage.error("Image upload failed.");
}
};
// 移除图片
const removeImage = (proofIndex: number, imageIndex: number) => {
if (proofData.value[proofIndex]?.images) {
proofData.value[proofIndex].images!.splice(imageIndex, 1);
if (proofData.value[proofIndex].images!.length === 0) {
proofData.value[proofIndex].displayUrl = "";
} else {
proofData.value[proofIndex].displayUrl =
proofData.value[proofIndex].images![0].url;
}
}
};
// 预览图片
const previewImage = (url: string) => {
// 使用 el-dialog 预览图片
previewImageUrl.value = url;
imagePreviewVisible.value = true;
};
// 切换展示图片
const changeDisplayImage = (proofIndex: number, url: string) => {
if (proofData.value[proofIndex]) {
proofData.value[proofIndex].displayUrl = url;
// 找到对应图片的文件ID并更新
const selectedImage = proofData.value[proofIndex].images?.find(
(img) => img.url === url
);
if (selectedImage?.fileId) {
proofData.value[proofIndex].fileId = selectedImage.fileId;
}
}
};
// 验证证明材料
const validateProofMaterials = () => {
for (let i = 0; i < approvalInformationObj.requiredProofTypes.length; i++) {
const proofType = approvalInformationObj.requiredProofTypes[i];
if (proofType.required) {
if (proofType.type === 0) {
// 图片上传类型检查
if (!proofData.value[i]?.images?.length) {
ElMessage.warning(`Please upload the supporting document for ${proofType.value}`);
return false;
}
} else if (proofType.type === 1) {
// 链接输入类型检查
if (!proofData.value[i]?.url?.trim()) {
ElMessage.warning(`Please enter the content for ${proofType.value}.`);
return false;
}
}
}
}
return true;
};
const applicationId = ref();
// 存储未勾选记录的ID列表
const uncheckedIds = ref<string[]>([]);
// 处理未勾选ID列表更新
const handleUncheckedIdsUpdate = (ids: string[]) => {
uncheckedIds.value = ids;
};
// 处理弹窗关闭事件
const handleDialogClosed = (uncheckedNumber: number) => {
// 弹窗关闭时调用接口,传递uncheckedNumber参数
if (currentQuarterData.value) {
getApprovalDetailsData(
selectedCategory.value || 0,
currentQuarterData.value.value.beginTime,
currentQuarterData.value.value.endTime,
false, // needCategoryList
uncheckedNumber // 传递未勾选数量
);
}
};
const handleSendToApproval = async () => {
// 验证证明材料
if (!validateProofMaterials()) {
return;
}
};
const handleSave = () => {
if (!commissionType.value) {
return ElMessage.warning("Commission Type cannot be empty.");
}
// 验证Time必填(当Time字段显示时)
const needTimeTypes = [
CommissionType.DE_JOB_GRADE,
CommissionType.AE_JOB_GRADE,
];
if (!needTimeTypes.includes(commissionType.value) && !selectedQuarter.value) {
return ElMessage.warning("Time cannot be empty.");
}
const needCategoryTypes = [
CommissionType.PAPER_EXTRA,
CommissionType.SI_EXTRA,
CommissionType.OTHER,
CommissionType.DE_JOB_GRADE,
CommissionType.AE_JOB_GRADE,
];
if (
needCategoryTypes.includes(commissionType.value) &&
!selectedCategory.value
) {
return ElMessage.warning("Category cannot be empty.");
}
// 验证paper id
if (
[CommissionType.PAPER_EXTRA].includes(commissionType.value) &&
!approvalInformationObj.paperId
) {
return ElMessage.warning("Paper ID cannot be empty.");
}
// 验证SI ID必填(当SI ID字段显示时)
const needSectionIdTypes = [CommissionType.SI_EXTRA];
if (
needSectionIdTypes.includes(commissionType.value) &&
!approvalInformationObj.sectionId?.trim()
) {
return ElMessage.warning("SI ID cannot be empty.");
}
// 验证Remark必填
if (!approvalInformationObj.remark?.trim()) {
return ElMessage.warning("Remark cannot be empty.");
}
// 验证证明材料
if (!validateProofMaterials()) {
return;
}
if(sameApplications.value && !approvalInformationObj.paperId){
return ElMessage.warning("Please note that this ID has already been submitted. ");
}
if(sameApplications.value && !approvalInformationObj.sectionId){
return ElMessage.warning("Please note that this ID has already been submitted. ");
}
// 构建证明材料数据 - 按照后端要求的格式
const proofRelations = approvalInformationObj.requiredProofTypes
.map((proofType, index) => {
const data = proofData.value[index];
if (proofType.type === 0) {
// 图片类型:如果有多张图片,每张图片作为一条数据
if (data?.images?.length > 0) {
return data.images.map((image) => ({
fileId: image.fileId || null,
note: "",
fileType: proofType.id,
}));
} else {
// 没有图片时返回一条空数据
// return [
// {
// fileId: null,
// note: "",
// fileType: proofType.id,
// },
// ];
}
} else if (proofType.type === 1) {
// 文本输入类型:只返回用户输入的文本,fileId为空
return [
{
fileId: null,
note: data?.url || "",
fileType: proofType.id,
},
];
}
return null;
})
.filter((item) => item !== null) // 过滤掉空值
.flat(); // 将嵌套数组展平
// 添加文件上传数据 (当Commission Type为Other且Category为26时)
const fileUploadRelations: any = [];
if (commissionType.value === CommissionType.OTHER && selectedCategory.value === 26 && uploadedFiles.value.length > 0) {
uploadedFiles.value.forEach((file) => {
const fileRelation = {
fileId: file.fileId || null,
note: "",
fileType: -1, // 文件类型固定传-1
};
fileUploadRelations.push(fileRelation);
});
}
// 合并证明材料和文件上传数据
const allProofRelations = [...proofRelations, ...fileUploadRelations];
const needPaperId = [CommissionType.PAPER_EXTRA, CommissionType.OTHER].includes(commissionType.value);
const needSectionId = [CommissionType.SI_EXTRA, CommissionType.OTHER].includes(commissionType.value);
ElMessageBox.confirm("Confirm the approval of this application?", "Tips", {
confirmButtonText: "Confirm",
cancelButtonText: "Cancel",
type: "success",
})
.then(() => {
performanceAppCreate(params).then((res: any) => {
if (res.code === 2000) {
ElMessage({
type: "success",
message: res.message,
});
applicationId.value = res.data.application?.id;
let routeData = router.resolve({
path: needTimeTypes.includes(commissionType.value)
? "/performanceManagement/jobGradeApprovals"
: "/performanceManagement/approvalRequestList",
query: {
approverEmail: personalInformationObj.email,
},
});
window.open(routeData.href, "_blank");
}
});
})
.catch(() => {});
};
const rulesDialogVisible = ref(false);
const performanceSettingData = ref();
// 图片预览相关
const imagePreviewVisible = ref(false);
const previewImageUrl = ref("");
// 添加显示Matching Rules详情的方法
const showMatchingRulesDetails = () => {
tableDialogTitle.value = "Matching Rules Details";
rulesDialogVisible.value = true;
performanceSettingsRuleId({
id: approvalInformationObj.performanceSettingId,
}).then((res) => {
if (res.code === 2000) {
performanceSettingData.value = res.data.performanceSetting;
}
});
};
const cancelModal = () => {
rulesDialogVisible.value = false
}
// 文件上传相关方法
const beforeFileUpload = (file: File) => {
const validExtensions = [
'.zip', '.rar', '.7z', '.tar', '.gz', // 压缩包
'.pdf', // PDF文档
'.doc', '.docx', // Word文档
'.xls', '.xlsx', // Excel文档
'.ppt', '.pptx', // PowerPoint文档
'.txt', '.csv', // 文本文件
'.json', '.xml' // 数据文件
];
const fileName = file.name.toLowerCase();
const isValidType = validExtensions.some(ext => fileName.endsWith(ext));
const isLt50M = file.size / 1024 / 1024 < 50;
if (!isValidType) {
ElMessage.error("Supported file types: .zip, .rar, .7z, .tar, .gz, .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .txt, .csv, .json, .xml");
return false;
}
if (!isLt50M) {
ElMessage.error("The file size must not exceed 50MB.");
return false;
}
return true;
};
// 自定义文件上传函数
const customFileUpload = async (options: any) => {
const file = options.file;
if (!beforeFileUpload(file)) return;
try {
const uploadResponse = await uploadFileApiNew(file);
if (uploadResponse.data) {
// 根据API响应结构获取正确的fileId
const fileId = uploadResponse.data.uploadFileData?.id || uploadResponse.data.id;
const fileData = {
name: file.name,
size: file.size,
fileId: fileId,
originalFile: file
};
uploadedFiles.value.push(fileData);
console.log("文件上传成功,fileId:", fileId, "文件数据:", fileData);
ElMessage.success("File uploaded successfully.");
} else {
ElMessage.error("File upload failed.");
}
} catch (error) {
console.error("Upload error:", error);
ElMessage.error("File upload failed.");
}
};
// 处理文件上传成功
const handleFileUploadSuccess = (response: any, file: any) => {
// 这个方法在使用 http-request 时不会被调用,但保留以防需要
};
// 移除已上传的文件
const removeUploadedFile = (index: number) => {
uploadedFiles.value.splice(index, 1);
ElMessage.success("File removed successfully.");
};
// 格式化文件大小
const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
</script>
- 测试数据
"requiredProofTypes": [
{
"description": "222222",
"id": 3,
"type": 0,
"value": "222222",
"required": true // 判断是否必填
},
{
"description": "链接",
"id": 4,
"type": 1,
"value": "Link",
"required": true
},
{
"description": "hhhhh",
"id": 5,
"type": 0,
"value": "hhhhh",
"required": true
}
],