update MeetingPage

This commit is contained in:
haomingming 2026-05-20 18:47:06 +08:00
parent 815aa04fe8
commit edffa25ccd

View File

@ -1,5 +1,5 @@
<template>
<PageContainer title="浼氳绠$悊">
<PageContainer title="会议管理">
<MeetingQueryToolbar v-model:query-form="queryForm" @load="load" @reset-query="resetQuery" />
<MeetingListTable
:rows="rows"
@ -392,9 +392,9 @@ const defaultMeetingForm = () => ({
projectName: "",
projectStartDate: "",
projectEndDate: "",
topic: "MVP浼氳",
topic: "MVP会议",
meetingCategory: "学术会",
meetingForm: "绾夸笅",
meetingForm: "线下",
location: "",
startTime: "",
endTime: "",
@ -421,7 +421,7 @@ const meetingFormTimeRange = computed<string[]>({
},
});
const materialDialogVisible = ref(false);
const materialDialogTitle = ref("浼氳璧勬枡");
const materialDialogTitle = ref("会议资料");
const selectedMeetingId = ref<number | null>(null);
const selectedMeetingAuditStatus = computed(() => {
const meetingId = Number(selectedMeetingId.value || 0);
@ -524,11 +524,11 @@ const meetingInvoiceSectionDefs: MeetingInvoiceSectionDef[] = [
{
code: "VENUE_CONFIRMATION",
order: 2,
title: "浼氬満纭鍑?浼氬満鍗忚",
title: "会场确认函/会场协议",
needAmount: true,
maxUploadCount: 5,
uploadFields: [
{ key: "invoiceFile", label: "鍙戠エ" },
{ key: "invoiceFile", label: "发票" },
{ key: "detailFile", label: "明细单" },
],
sampleImage: [VenueSampleImage, VenueAgreementSampleImage],
@ -536,36 +536,36 @@ const meetingInvoiceSectionDefs: MeetingInvoiceSectionDef[] = [
{
code: "CONSTRUCTION_DETAIL",
order: 3,
title: "浼氳鎼缓鏄庣粏",
title: "会议搭建明细",
needAmount: true,
maxUploadCount: 15,
uploadFields: [
{ key: "invoiceFile", label: "鍙戠エ" },
{ key: "invoiceFile", label: "发票" },
{ key: "detailFile", label: "明细单" },
{ key: "equipmentFile", label: "璁惧鐓х墖" },
{ key: "equipmentFile", label: "设备照片" },
],
sampleImage: [ConstructionSampleImage, EquipmentSampleImage2]
},
{
code: "ACCOMMODATION_DETAIL",
order: 4,
title: "浣忓鏄庣粏",
title: "住宿明细",
needAmount: true,
maxUploadCount: 50,
uploadFields: [
{ key: "invoiceFile", label: "鍙戠エ" },
{ key: "invoiceFile", label: "发票" },
{ key: "detailFile", label: "明细单" },
],
},
{
code: "CATERING_DETAIL",
order: 5,
title: "椁愰ギ鏄庣粏",
title: "餐饮明细",
needAmount: true,
maxUploadCount: 50,
uploadFields: [
{ key: "invoiceFile", label: "鍙戠エ" },
{ key: "detailFile", label: "鏄庣粏鍗?姘村崟" },
{ key: "invoiceFile", label: "发票" },
{ key: "detailFile", label: "明细单/水单" },
],
sampleImage: CateringSampleImage,
},
@ -576,7 +576,7 @@ const meetingInvoiceSectionDefs: MeetingInvoiceSectionDef[] = [
needAmount: true,
maxUploadCount: 50,
uploadFields: [
{ key: "invoiceFile", label: "鍙戠エ" },
{ key: "invoiceFile", label: "发票" },
{ key: "detailFile", label: "明细单" },
],
sampleImage: [LocalTransportSampleImage, LocalTransportSampleImage2, LocalTransportSampleImage3],
@ -588,7 +588,7 @@ const meetingInvoiceSectionDefs: MeetingInvoiceSectionDef[] = [
needAmount: true,
maxUploadCount: 50,
uploadFields: [
{ key: "invoiceFile", label: "鍙戠エ" },
{ key: "invoiceFile", label: "发票" },
{ key: "detailFile", label: "明细单" },
],
sampleImage: [IntercityTransportSampleImage, IntercityTransportSampleImage2, IntercityTransportSampleImage3, IntercityTransportSampleImage4],
@ -596,13 +596,13 @@ const meetingInvoiceSectionDefs: MeetingInvoiceSectionDef[] = [
{
code: "MATERIAL_DETAIL",
order: 8,
title: "鐗╂枡鏄庣粏",
title: "物料明细",
needAmount: true,
maxUploadCount: 50,
uploadFields: [
{ key: "invoiceFile", label: "鍙戠エ" },
{ key: "invoiceFile", label: "发票" },
{ key: "detailFile", label: "明细单" },
{ key: "materialFile", label: "鐗╂枡鐓х墖" },
{ key: "materialFile", label: "物料照片" },
],
sampleImage: [MaterialSampleImage, MaterialSampleImage2, MaterialSampleImage3, MaterialSampleImage4, MaterialSampleImage5, MaterialSampleImage6],
},
@ -613,7 +613,7 @@ const meetingInvoiceSectionDefs: MeetingInvoiceSectionDef[] = [
needAmount: true,
maxUploadCount: 50,
uploadFields: [
{ key: "invoiceFile", label: "鍙戠エ" },
{ key: "invoiceFile", label: "发票" },
{ key: "detailFile", label: "明细单" },
{ key: "designDraftFile", label: "设计稿" },
],
@ -622,15 +622,15 @@ const meetingInvoiceSectionDefs: MeetingInvoiceSectionDef[] = [
{
code: "OTHER",
order: 10,
title: "鍏朵粬",
title: "其他",
needAmount: true,
maxUploadCount: 5,
uploadFields: [
{ key: "invoiceFile", label: "鍏朵粬闄勪欢1" },
{ key: "detailFile", label: "鍏朵粬闄勪欢2" },
{ key: "equipmentFile", label: "鍏朵粬闄勪欢3" },
{ key: "materialFile", label: "鍏朵粬闄勪欢4" },
{ key: "designDraftFile", label: "鍏朵粬闄勪欢5" },
{ key: "invoiceFile", label: "其他附件1" },
{ key: "detailFile", label: "其他附件2" },
{ key: "equipmentFile", label: "其他附件3" },
{ key: "materialFile", label: "其他附件4" },
{ key: "designDraftFile", label: "其他附件5" },
],
},
];
@ -765,7 +765,7 @@ const filterMeetingInvoiceSectionDefsByMeetingForm = (
sectionDefs: MeetingInvoiceSectionDef[] = meetingInvoiceSectionDefs,
) => {
const meetingForm = String(meetingFormRaw || "").trim();
if (meetingForm !== "绾夸笂") {
if (meetingForm !== "线上") {
return sectionDefs;
}
const otherSection = sectionDefs.find((def) => def.code === "OTHER");
@ -785,7 +785,7 @@ const resolveSelectedMeetingForm = () => {
};
const filteredMeetingInvoiceSectionDefs = computed(() => {
const meetingForm = resolveSelectedMeetingForm();
if (meetingForm === "绾夸笂") {
if (meetingForm === "线上") {
return filterMeetingInvoiceSectionDefsByMeetingForm(meetingForm);
}
const meetingId = Number(selectedMeetingId.value || 0);
@ -1142,10 +1142,10 @@ const resolveReviewResult = (itemKey: string | string[]) => resolveReviewField(i
const toReviewResultText = (itemKey: string | string[]) => {
const result = resolveReviewResult(itemKey);
if (result === "APPROVED") {
return "宸查€氳繃";
return "已通过";
}
if (result === "REJECTED") {
return "涓嶉€氳繃";
return "已拒绝";
}
return "";
};
@ -1168,7 +1168,7 @@ const buildMeetingInvoiceFieldItemKey = (sectionCode: MeetingInvoiceSectionCode,
`meeting_invoice:${sectionCode}:${fieldKey}`;
const buildMeetingInvoiceAmountItemKey = (sectionCode: MeetingInvoiceSectionCode) => `meeting_invoice:${sectionCode}:amount`;
const warnReviewedMaterialLocked = () => {
ElMessage.warning("璇ヤ細璁祫鏂欎腑鐨勫凡瀹℃牳閫氳繃椤逛笉鍏佽淇敼");
ElMessage.warning("该会议资料中的已审核通过项不允许修改");
};
const warnMeetingMaterialReadonly = () => {
ElMessage.warning("该会议资料审核中或已审核通过,不允许再修改");
@ -1244,7 +1244,7 @@ const isImageDocFile = (name: string, ossKey: string) => isImageFile(name || "")
const isPdfDocFile = (name: string, ossKey: string) => extractExt(name || "") === ".pdf" || extractExt(ossKey || "") === ".pdf";
const agendaUploadFileList = computed<UploadUserFile[]>(() =>
agendaDocRows.value.map((item, index) => {
const name = item.name || `浼氳鏃ョ▼${index + 1}`;
const name = item.name || `会议日程${index + 1}`;
const isImage = isImageDocFile(name, item.ossKey);
return {
name,
@ -1273,7 +1273,7 @@ const themePhotoUploadFileList = computed<UploadUserFile[]>(() => {
if (!docForm.value.themePhotoOssKey) {
return [];
}
const name = displayDocName(docForm.value.themePhotoName, docForm.value.themePhotoOssKey, "涓婚鐓х墖");
const name = displayDocName(docForm.value.themePhotoName, docForm.value.themePhotoOssKey, "主题照片");
const isImage = isImageDocFile(name, docForm.value.themePhotoOssKey);
return [{
name,
@ -1287,7 +1287,7 @@ const invitationUploadFileList = computed<UploadUserFile[]>(() =>
invitationDocRows.value.map((item, index) => {
const isImage = isImageDocFile(item.name, item.ossKey);
return {
name: item.name || `閭€璇峰嚱${index + 1}`,
name: item.name || `邀请函${index + 1}`,
url: isImage ? (docPreviewUrlMap.value[item.ossKey] || "") : "",
status: "success",
uid: index + 1,
@ -1300,7 +1300,7 @@ const expertProfileUploadFileList = computed<UploadUserFile[]>(() => {
if (!ossKey) {
return [];
}
const fileName = String(expertProfileForm.value.fileName || "").trim() || "涓撳绠€浠?涓插満";
const fileName = String(expertProfileForm.value.fileName || "").trim() || "专家简介/串场";
return [{
name: fileName,
status: "success",
@ -1318,7 +1318,7 @@ const laborProtocolUploadFileList = computed<UploadUserFile[]>(() => {
if (!row || !ossKey) {
return [];
}
const fileName = String(row.protocolFileName || "").trim() || String(row.protocolName || "").trim() || "鍗忚鏂囦欢";
const fileName = String(row.protocolFileName || "").trim() || String(row.protocolName || "").trim() || "协议文件";
const preview = String(row.protocolPreviewUrl || "").trim();
const isImage = isImageDocFile(fileName, ossKey);
return [{
@ -1341,7 +1341,7 @@ const laborInvoiceUploadFileList = computed<UploadUserFile[]>(() => {
}
return files.map((file, index) => {
const ossKey = String(file?.ossKey || "").trim();
const fileName = String(file?.fileName || "").trim() || `鍔冲姟鍙戠エ${index + 1}`;
const fileName = String(file?.fileName || "").trim() || `劳务发票${index + 1}`;
const preview = String(file?.previewUrl || "").trim();
const isImage = isImageDocFile(fileName, ossKey);
return {
@ -1398,7 +1398,7 @@ const getMeetingInvoiceFileRow = (
};
const ocrRawDialogVisible = ref(false);
const ocrRawDialogTitle = ref("OCR鍘熷缁撴灉");
const ocrRawDialogTitle = ref("OCR原始结果");
const ocrRawDialogText = ref("");
const laborInvoiceOcrConfirmVisible = ref(false);
const laborAgreementExtractConfirmVisible = ref(false);
@ -1500,7 +1500,7 @@ const mergeLaborRow = (current: LaborRow | null | undefined, incoming: LaborRow)
const openOcrRawDialog = (sectionCode: MeetingInvoiceSectionCode, fieldKey: MeetingInvoiceUploadFieldKey) => {
const row = getMeetingInvoiceFileRow(sectionCode, fieldKey);
const raw = row?.ocr?.raw;
ocrRawDialogTitle.value = `OCR鍘熷缁撴灉 - ${sectionCode}/${fieldKey}`;
ocrRawDialogTitle.value = `OCR原始结果 - ${sectionCode}/${fieldKey}`;
try {
ocrRawDialogText.value = raw ? JSON.stringify(raw, null, 2) : "";
} catch (_e) {
@ -1580,7 +1580,7 @@ const resolveCurrentExpertName = (expertId: number) => {
const runLaborInvoiceOcr = async (targetOssKey?: string) => {
const expertId = Number(selectedExpertMaterialId.value || 0);
if (!expertId) {
ElMessage.warning("鏈€夋嫨涓撳");
ElMessage.warning("未选择专家");
return;
}
const row = getCurrentLaborRow(expertId);
@ -1607,7 +1607,7 @@ const runLaborInvoiceOcr = async (targetOssKey?: string) => {
const totalAmountCent = Number(normalized?.totalAmountCent || 0);
const taxCent = Number(normalized?.taxCent || 0);
console.groupCollapsed("[LaborInvoiceOCR] 濮撳悕姣斿璋冭瘯");
console.groupCollapsed("[LaborInvoiceOCR] 姓名比对调试");
console.info("expertId =", expertId);
console.info("expertName =", expertName);
console.info("normalized.name =", normalizedName);
@ -1649,10 +1649,10 @@ const runLaborInvoiceOcr = async (targetOssKey?: string) => {
status: "failed",
normalized: row.invoiceOcr?.normalized,
raw: row.invoiceOcr?.raw,
message: String(e?.message || "鍔冲姟鍙戠エOCR璇嗗埆澶辫触"),
message: String(e?.message || "劳务发票OCR识别失败"),
updatedAt: new Date().toISOString(),
};
ElMessage.error(row.invoiceOcr.message || "鍔冲姟鍙戠エOCR璇嗗埆澶辫触");
ElMessage.error(row.invoiceOcr.message || "劳务发票OCR识别失败");
}
};
@ -1679,7 +1679,7 @@ const confirmLaborInvoiceOcrFill = () => {
if (!laborInvoiceOcrConfirmData.value.nameMatched) {
ElMessage.warning(`劳务发票姓名识别为“${laborInvoiceOcrConfirmData.value.ocrName || "-"}”,与当前专家“${laborInvoiceOcrConfirmData.value.expertName || "-"}”不一致,请核对。`);
} else {
ElMessage.success("宸茬璁ゅ苟鍥炲鍔冲姟鍙戠エOCR缁撴灉");
ElMessage.success("已确认并回填劳务发票OCR结果");
}
laborInvoiceOcrConfirmVisible.value = false;
};
@ -1755,10 +1755,10 @@ const runMeetingInvoiceOcr = async (sectionCode: MeetingInvoiceSectionCode, fiel
status: "failed",
normalized: row.ocr?.normalized,
raw: row.ocr?.raw,
message: String(e?.message || "OCR璇嗗埆澶辫触"),
message: String(e?.message || "OCR识别失败"),
updatedAt: new Date().toISOString(),
};
ElMessage.error(row.ocr.message || "OCR璇嗗埆澶辫触");
ElMessage.error(row.ocr.message || "OCR识别失败");
}
};
@ -1787,7 +1787,7 @@ const confirmMeetingInvoiceOcrFill = () => {
const newAmountYuan = Number((Number(meetingInvoiceOcrConfirmData.value.totalAmountCent || 0) / 100).toFixed(2));
section.totalAmountYuan = Number((currentAmountYuan + newAmountYuan).toFixed(2));
}
ElMessage.success("宸茬璁ゅ苟鍥炲浼氳鍙戠エOCR缁撴灉");
ElMessage.success("已确认并回填会议发票OCR结果");
meetingInvoiceOcrConfirmVisible.value = false;
};
@ -1815,7 +1815,7 @@ const meetingInvoiceOtherUploadFileList = computed<UploadUserFile[]>(() =>
if (!ossKey) {
return null;
}
const name = String(row?.fileName || "").trim() || `鍏朵粬闄勪欢${index + 1}-${rowIndex + 1}`;
const name = String(row?.fileName || "").trim() || `其他附件${index + 1}-${rowIndex + 1}`;
const isImage = isImageDocFile(name, ossKey);
return {
name,
@ -1991,7 +1991,7 @@ const handleSelectExpertForMaterial = (row: any) => {
const expertModuleDoneText = (expertId: number) => {
const id = Number(expertId || 0);
if (!id) {
return "鏈~";
return "未填";
}
if (selectedModuleCode.value === "EXPERT_LIST" && expertSubModule.value === "ONSITE_PHOTO") {
return `${(photoByExpert.value[id] || []).length}`;
@ -1999,7 +1999,7 @@ const expertModuleDoneText = (expertId: number) => {
if (selectedModuleCode.value === "EXPERT_LIST" && expertSubModule.value === "LABOR_PROTOCOL") {
const row = laborByExpert.value[id];
if (!row || isLaborRowEmpty(row)) {
return "鏈~";
return "未填";
}
return row.protocolOssKey ? "已上传协议" : "已填写";
}
@ -2015,7 +2015,7 @@ const putToSignedUrl = async (url: string, file: File, contentType?: string) =>
body: file,
});
if (!result.ok) {
throw new Error(`涓婁紶澶辫触: ${result.status}`);
throw new Error(`上传失败: ${result.status}`);
}
};
@ -2134,7 +2134,7 @@ const ensureSummaryTaskState = async (meetingId: number) => {
const showSummaryTaskStatusMessage = (state: SummaryTaskState | null | undefined) => {
if (!state?.taskId) {
ElMessage.warning("璇峰厛鐢熸垚浼氳鎬荤粨浠诲姟");
ElMessage.warning("请先生成会议总结任务");
return;
}
const status = String(state.status || "").toUpperCase();
@ -2143,10 +2143,10 @@ const showSummaryTaskStatusMessage = (state: SummaryTaskState | null | undefined
return;
}
if (status === "FAILED") {
ElMessage.error(state.errorMessage ? `浼氳鎬荤粨浠诲姟澶辫触锛${state.errorMessage}` : "浼氳鎬荤粨浠诲姟澶辫触");
ElMessage.error(state.errorMessage ? `会议总结任务失败:${state.errorMessage}` : "会议总结任务失败");
return;
}
ElMessage.info(`浼氳鎬荤粨浠诲姟褰撳墠鐘舵€侊細${toZhStatus(status || "PENDING")}`);
ElMessage.info(`会议总结任务当前状态:${toZhStatus(status || "PENDING")}`);
};
const syncSearchRoute = async () => {
@ -2252,7 +2252,7 @@ const handleUpdate = async () => {
const nextBudgetCent = toCent(meetingForm.value.budgetYuan);
const prevBudgetCent = Math.max(0, Number(currentEditOriginalBudgetCent.value || 0));
if (!currentEditAllowMeetingOverBudget.value && nextBudgetCent > prevBudgetCent) {
ElMessage.warning("褰撳墠椤圭洰涓嶅厑璁镐細璁绠楄皟楂橈紝浠呮敮鎸佷繚鎸佷笉鍙樻垨璋冧綆棰勭畻");
ElMessage.warning("当前项目不允许会议预算调高,仅支持保持不变或调低预算");
return;
}
await updateMeeting(currentEditMeetingId.value, {
@ -2267,17 +2267,17 @@ const handleUpdate = async () => {
laborRatio: meetingForm.value.laborRatio,
cateringRatio: meetingForm.value.cateringRatio,
});
ElMessage.success("浼氳缂栬緫鎴愬姛");
ElMessage.success("会议编辑成功");
editDrawerVisible.value = false;
await load();
};
const REQUIRED_MODULES = [
{ code: "BASIC_INFO", name: "浼氳鍩烘湰淇℃伅妯″潡" },
{ code: "WRITE_OFF_DOCS", name: "鏍搁攢鏉愭枡妯″潡" },
{ code: "EXPERT_PROFILE", name: "涓撳绠€浠?涓插満妯″潡" },
{ code: "EXPERT_LIST", name: "涓撳鍒楄〃妯″潡" },
{ code: "MEETING_INVOICE", name: "浼氳鍙戠エ妯″潡" },
{ code: "BASIC_INFO", name: "会议基本信息模块" },
{ code: "WRITE_OFF_DOCS", name: "核销材料模块" },
{ code: "EXPERT_PROFILE", name: "专家简介/串场模块" },
{ code: "EXPERT_LIST", name: "专家列表模块" },
{ code: "MEETING_INVOICE", name: "会议发票模块" },
];
const handleSubmit = async (meetingId: number) => {
@ -2305,7 +2305,7 @@ const handleSubmit = async (meetingId: number) => {
if (missingModuleNames.length > 0) {
ElMessageBox.alert(`请先完善以下模块后再提交审核:${missingModuleNames.join("、")}`, "提示", {
confirmButtonText: "纭畾",
confirmButtonText: "确定",
type: "warning",
});
return;
@ -2322,7 +2322,7 @@ const handleSubmit = async (meetingId: number) => {
const handleWithdraw = async (meetingId: number) => {
await withdrawMeeting(meetingId, {
idempotencyKey: `withdraw-${Date.now()}-${meetingId}`,
reason: "浼氳鎾ゅ洖鎻愪氦",
reason: "会议撤回提交",
});
ElMessage.success("会议已撤回到待提交状态");
await load();
@ -2331,8 +2331,8 @@ const handleWithdraw = async (meetingId: number) => {
const handleDeleteDraft = async (meetingId: number) => {
await ElMessageBox.confirm("确认删除该草稿会议?删除后不可恢复。", "删除草稿", {
type: "warning",
confirmButtonText: "纭鍒犻櫎",
cancelButtonText: "鍙栨秷",
confirmButtonText: "确认删除",
cancelButtonText: "取消",
});
await deleteMeeting(meetingId);
ElMessage.success("草稿会议已删除");
@ -2341,10 +2341,10 @@ const handleDeleteDraft = async (meetingId: number) => {
const handleCancelMeeting = async (meetingId: number) => {
const result = await ElMessageBox.prompt("请输入取消原因", "取消会议", {
confirmButtonText: "纭鍙栨秷",
cancelButtonText: "杩斿洖",
confirmButtonText: "确认取消",
cancelButtonText: "返回",
inputPlaceholder: "请输入取消原因",
inputValidator: (val: string) => (val && val.trim() ? true : "鍙栨秷鍘熷洜涓嶈兘涓虹┖"),
inputValidator: (val: string) => (val && val.trim() ? true : "取消原因不能为空"),
}).catch(() => null);
if (!result) return;
await cancelMeeting(meetingId, { reason: result.value });
@ -2358,13 +2358,13 @@ const handleExportMaterials = async (meetingId: number) => {
fileName: buildMeetingExportFileName(meetingId, "会议资料包", ".zip"),
});
const taskId = Number(resp?.data?.taskId || 0);
ElMessage.success(taskId > 0 ? `璧勬枡鍖呭鍑轰换鍔″凡鍒涘缓锛${taskId}锛岃鍓嶅線瀵煎嚭浠诲姟涓績涓嬭浇` : "璧勬枡鍖呭鍑轰换鍔″凡鍒涘缓锛岃鍓嶅線瀵煎嚭浠诲姟涓績涓嬭浇");
ElMessage.success(taskId > 0 ? `资料包导出任务已创建:${taskId},请前往导出任务中心下载` : "资料包导出任务已创建,请前往导出任务中心下载");
};
const handleGenerateSummary = async (meetingId: number) => {
const resp = await generateMeetingSummaryTask(meetingId, {
idempotencyKey: `summary-generate-${meetingId}-${Date.now()}`,
fileName: buildMeetingExportFileName(meetingId, "浼氳鎬荤粨", ".docx"),
fileName: buildMeetingExportFileName(meetingId, "会议总结", ".docx"),
});
const taskId = Number(resp?.data?.taskId || 0);
if (taskId > 0) {
@ -2390,7 +2390,7 @@ const handleRefreshSummaryToken = async (meetingId: number) => {
const state = await ensureSummaryTaskState(meetingId);
const taskId = Number(state?.taskId || 0);
if (!taskId) {
ElMessage.warning("璇峰厛鐢熸垚浼氳鎬荤粨浠诲姟");
ElMessage.warning("请先生成会议总结任务");
return;
}
const status = String(state?.status || "").toUpperCase();
@ -2450,7 +2450,7 @@ const loadBoundExperts = async (meetingId: number, requestId?: number) => {
const list = resp?.data || [];
boundExpertRows.value = list.map((item: any) => ({
expertId: item?.expertId,
// ?
//
expertName: item?.expertNameMasked || item?.maskedExpertName || item?.expertName || "",
title: item?.title || "",
organization: item?.organizationMasked || item?.maskedOrganization || item?.organization || "",
@ -2488,14 +2488,14 @@ const loadPlatformExperts = async () => {
if (!id) {
continue;
}
selectedPlatformExpertNameMap.value[id] = String(item?.expertName || "").trim() || `涓撳#${id}`;
selectedPlatformExpertNameMap.value[id] = String(item?.expertName || "").trim() || `专家#${id}`;
}
const rowIdSet = new Set(platformExpertRows.value.map((item: any) => Number(item?.id || 0)).filter((id: number) => id > 0));
currentSearchSelectedPlatformExpertIds.value = selectedPlatformExpertIds.value.filter((id) => rowIdSet.has(id));
} catch (e: any) {
platformExpertRows.value = [];
currentSearchSelectedPlatformExpertIds.value = [];
ElMessage.error(e?.response?.data?.message || "骞冲彴涓撳鍔犺浇澶辫触");
ElMessage.error(e?.response?.data?.message || "平台专家加载失败");
} finally {
platformExpertSearched.value = true;
}
@ -2541,7 +2541,7 @@ const handleCurrentSearchExpertSelectChange = (checkedIds: Array<number | string
if (removedBoundIds.length > 0) {
removedBoundIds.forEach((id) => checkedIdSet.add(id));
currentSearchSelectedPlatformExpertIds.value = Array.from(checkedIdSet);
ElMessage.warning("宸茬粦瀹氫笓瀹惰鍏堣蛋瑙g粦娴佺▼锛屼笉鑳界洿鎺ュ湪妫€绱㈢粨鏋滀腑鍒犻櫎");
ElMessage.warning("已绑定专家请先走解绑流程,不能直接在搜索结果中删除");
}
const nextSelected = selectedPlatformExpertIds.value
.filter((id) => !rowIdSet.has(id));
@ -2649,7 +2649,7 @@ const runCreateExpertIdOcr = async (side: "front" | "back", objectKey: string) =
};
createExpertIdOcrDialogVisible.value = true;
} catch (e: any) {
ElMessage.warning(e?.response?.data?.message || e?.message || "韬唤璇丱CR璇嗗埆澶辫触");
ElMessage.warning(e?.response?.data?.message || e?.message || "身份证OCR识别失败");
}
};
@ -2663,7 +2663,7 @@ const confirmCreateExpertIdOcrFill = () => {
}
}
createExpertIdOcrDialogVisible.value = false;
ElMessage.success("宸茬璁ゅ苟鍥炲韬唤璇丱CR缁撴灉");
ElMessage.success("已确认并回填身份证OCR结果");
};
const cancelCreateExpertIdOcrFill = () => {
@ -2683,7 +2683,7 @@ const runCreateExpertBankCardOcr = async (objectKey: string) => {
};
createExpertBankCardOcrDialogVisible.value = true;
} catch (e: any) {
ElMessage.warning(e?.response?.data?.message || e?.message || "閾惰鍗CR璇嗗埆澶辫触");
ElMessage.warning(e?.response?.data?.message || e?.message || "银行卡OCR识别失败");
}
};
@ -2698,7 +2698,7 @@ const confirmCreateExpertBankCardOcrFill = () => {
createPlatformExpertForm.value.accountName = createExpertBankCardOcrData.value.accountName;
}
createExpertBankCardOcrDialogVisible.value = false;
ElMessage.success("宸茬璁ゅ苟鍥炲閾惰鍗CR缁撴灉");
ElMessage.success("已确认并回填银行卡OCR结果");
};
const cancelCreateExpertBankCardOcrFill = () => {
@ -2707,7 +2707,7 @@ const cancelCreateExpertBankCardOcrFill = () => {
const uploadCreateExpertAsset = async (assetType: "ID_FRONT" | "ID_BACK" | "CARD") => {
if (!selectedExpertManageMeetingId.value) {
ElMessage.warning("璇峰厛閫夋嫨浼氳鍚庡啀鏂板涓撳");
ElMessage.warning("请先选择会议后再新增专家");
return;
}
const file =
@ -2717,7 +2717,7 @@ const uploadCreateExpertAsset = async (assetType: "ID_FRONT" | "ID_BACK" | "CARD
? createExpertIdBackFile.value
: createExpertCardFrontFile.value;
if (!file) {
ElMessage.warning("璇峰厛閫夋嫨鏂囦欢");
ElMessage.warning("请先选择文件");
return;
}
const signResp = await fetchMeetingMaterialUploadSign(selectedExpertManageMeetingId.value, "BASIC_INFO", {
@ -2727,7 +2727,7 @@ const uploadCreateExpertAsset = async (assetType: "ID_FRONT" | "ID_BACK" | "CARD
const uploadUrl = signResp?.data?.uploadUrl;
const objectKey = signResp?.data?.objectKey;
if (!uploadUrl || !objectKey) {
ElMessage.error("鑾峰彇涓婁紶绛惧悕澶辫触");
ElMessage.error("获取上传签名失败");
return;
}
await putToSignedUrl(uploadUrl, file, signResp?.data?.contentType || file.type);
@ -2747,7 +2747,7 @@ const uploadCreateExpertAsset = async (assetType: "ID_FRONT" | "ID_BACK" | "CARD
createExpertCardFrontFile.value = null;
await runCreateExpertBankCardOcr(objectKey);
}
ElMessage.success("涓婁紶鎴愬姛");
ElMessage.success("上传成功");
};
const loadExpertDictOptions = async () => {
@ -2771,31 +2771,31 @@ const handleCreatePlatformExpert = async () => {
return;
}
if (!createPlatformExpertForm.value.phone.trim()) {
ElMessage.warning("璇峰~鍐欐墜鏈哄彿");
ElMessage.warning("请填写手机号");
return;
}
if (!createPlatformExpertForm.value.titleCode.trim()) {
ElMessage.warning("璇烽€夋嫨鑱岀О");
ElMessage.warning("请选择职称");
return;
}
if (!createPlatformExpertForm.value.hospitalCode.trim()) {
ElMessage.warning("璇烽€夋嫨鍖婚櫌");
ElMessage.warning("请选择医院");
return;
}
if (!createPlatformExpertForm.value.bankName.trim()) {
ElMessage.warning("璇峰~鍐欏紑鎴疯");
ElMessage.warning("请填写开户行");
return;
}
if (!createPlatformExpertForm.value.bankProvince.trim()) {
ElMessage.warning("璇峰~鍐欏紑鎴疯鐪佷唤");
ElMessage.warning("请填写开户行省份");
return;
}
if (!createPlatformExpertForm.value.bankCity.trim()) {
ElMessage.warning("璇峰~鍐欏紑鎴疯鍩庡競");
ElMessage.warning("请填写开户行城市");
return;
}
if (!createPlatformExpertForm.value.bankBranchName.trim()) {
ElMessage.warning("璇峰~鍐欒缁嗗紑鎴疯");
ElMessage.warning("请填写详细开户行");
return;
}
if (!createPlatformExpertForm.value.bankCardNo.trim()) {
@ -2803,7 +2803,7 @@ const handleCreatePlatformExpert = async () => {
return;
}
if (!createPlatformExpertForm.value.accountName.trim()) {
ElMessage.warning("璇峰~鍐欏紑鎴峰悕");
ElMessage.warning("请填写开户名");
return;
}
const selectedTitle = expertTitleOptions.value.find((item) => item.dictCode === createPlatformExpertForm.value.titleCode);
@ -2854,7 +2854,7 @@ const openBindExperts = async (row: any) => {
if (!id) {
continue;
}
selectedPlatformExpertNameMap.value[id] = String(item?.expertName || "").trim() || `涓撳#${id}`;
selectedPlatformExpertNameMap.value[id] = String(item?.expertName || "").trim() || `专家#${id}`;
}
selectedPlatformExpertIds.value = profiles
.map((item: any) => Number(item?.expertId || 0))
@ -2875,7 +2875,7 @@ const handleBindExperts = async () => {
await bindMeetingExperts(meetingId, {
expertIds: selectedPlatformExpertIds.value,
});
ElMessage.success("涓撳缁戝畾鎴愬姛");
ElMessage.success("专家绑定成功");
await loadBoundExperts(meetingId);
bindExpertVisible.value = false;
};
@ -2890,9 +2890,9 @@ const handleUnbindExpert = async (row: any) => {
}
const confirm = await ElMessageBox.confirm(
`确认解绑专家「${row?.expertName || row?.expertId || ""}」吗?`,
"瑙g粦纭",
"解绑确认",
{
confirmButtonText: "纭畾",
confirmButtonText: "确定",
cancelButtonText: "鍙栨秷",
type: "warning",
},
@ -2903,7 +2903,7 @@ const handleUnbindExpert = async (row: any) => {
try {
const targetId = Number(row?.expertId || 0);
await unbindMeetingExpert(selectedExpertManageMeetingId.value, targetId);
ElMessage.success("瑙g粦鎴愬姛");
ElMessage.success("解绑成功");
await loadBoundExperts(selectedExpertManageMeetingId.value);
removeSelectedPlatformExpertLocal(targetId);
} catch (_e) {
@ -2928,10 +2928,10 @@ const pollMeetingLaborAgreementExtract = async (meetingId: number, taskId: strin
return data;
}
if (status === "FAILED") {
throw new Error(String(data?.reason || "鍔冲姟鍗忚鎶藉彇澶辫触"));
throw new Error(String(data?.reason || "劳务协议抽取失败"));
}
}
throw new Error("鍔冲姟鍗忚鎶藉彇瓒呮椂锛岃绋嶅悗閲嶈瘯");
throw new Error("劳务协议抽取超时,请稍后重试");
};
const openMeetingLaborAgreementExtractConfirm = (
@ -3025,7 +3025,7 @@ const handleMeetingLaborAgreementFilePicked = async (event: Event) => {
warnMeetingMaterialReadonly();
return;
}
if (!validateMaterialFile(file, "鍔冲姟鍗忚", 50)) {
if (!validateMaterialFile(file, "劳务协议", 50)) {
return;
}
meetingLaborAgreementUploading.value = true;
@ -3048,7 +3048,7 @@ const handleMeetingLaborAgreementFilePicked = async (event: Event) => {
if (!taskId) {
throw new Error("未获取到OCR任务号");
}
ElMessage.info("鍔冲姟鍗忚宸蹭笂浼狅紝姝e湪鎶藉彇涓撳淇℃伅");
ElMessage.info("劳务协议已上传,正在抽取专家信息");
const extracted = await pollMeetingLaborAgreementExtract(meetingId, taskId);
await applyMeetingLaborAgreementExtractFlow(meetingId, taskId, extracted);
} catch (error: any) {
@ -3098,11 +3098,11 @@ const handleUnbindExpertGuarded = async (row: any) => {
};
const moduleTitleMap: Record<string, string> = {
BASIC_INFO: "浼氳鍩烘湰淇℃伅妯″潡",
WRITE_OFF_DOCS: "鏍搁攢鏉愭枡妯″潡",
EXPERT_PROFILE: "涓撳绠€浠?涓插満妯″潡",
EXPERT_LIST: "涓撳鍒楄〃妯″潡",
MEETING_INVOICE: "浼氳鍙戠エ妯″潡",
BASIC_INFO: "会议基本信息模块",
WRITE_OFF_DOCS: "核销材料模块",
EXPERT_PROFILE: "专家简介/串场模块",
EXPERT_LIST: "专家列表模块",
MEETING_INVOICE: "会议发票模块",
};
type BackendMaterialModuleCode = "BASIC_INFO" | "WRITE_OFF_DOCS" | "EXPERT_PROFILE" | "EXPERT_LIST" | "MEETING_INVOICE";
const resolveBackendMaterialModuleCode = (moduleCode: MaterialModuleCode): BackendMaterialModuleCode => {
@ -3136,8 +3136,8 @@ const isMaterialLoadActive = (
};
const prepareMaterialModuleLoad = (moduleCode: MaterialModuleCode) => {
materialDialogTitle.value = moduleTitleMap[moduleCode] || "浼氳璧勬枡妯″潡";
materialReviewNotice.value = "瀹℃牳淇℃伅鍔犺浇涓?..";
materialDialogTitle.value = moduleTitleMap[moduleCode] || "会议资料模块";
materialReviewNotice.value = "审核信息加载中...";
};
const extractExt = (fileName: string) => {
@ -3405,7 +3405,7 @@ const validateImageFile = (file: File | null, label: string, maxSizeMb = MATERIA
return false;
}
if (Number(file.size || 0) > maxSizeMb * 1024 * 1024) {
ElMessage.warning(`${label}澶у皬涓嶈兘瓒呰繃${maxSizeMb}MB`);
ElMessage.warning(`${label}大小不能超过${maxSizeMb}MB`);
return false;
}
return true;
@ -3415,7 +3415,7 @@ const validateMaterialFile = (file: File | null, label: string, maxSizeMb = MATE
return false;
}
if (!isMaterialUploadAllowed(file)) {
ElMessage.warning(`${label}浠呮敮鎸佸浘鐗囨垨PDF鏂囦欢`);
ElMessage.warning(`${label}仅支持图片或PDF文件`);
return false;
}
if (Number(file.size || 0) > maxSizeMb * 1024 * 1024) {
@ -3433,17 +3433,17 @@ const validateExpertProfileFile = (file: File | null, maxSizeMb = MATERIAL_MAX_F
const isPptMime = mime === "application/vnd.ms-powerpoint"
|| mime === "application/vnd.openxmlformats-officedocument.presentationml.presentation";
if (!EXPERT_PROFILE_ACCEPT_EXT.has(ext) && mime !== "application/pdf" && !isPptMime) {
ElMessage.warning("涓撳绠€浠?涓插満浠呮敮鎸丳DF鎴朠PT鏂囦欢");
ElMessage.warning("专家简介/串场仅支持PDF或PPT文件");
return false;
}
if (Number(file.size || 0) > maxSizeMb * 1024 * 1024) {
ElMessage.warning(`涓撳绠€浠?涓插満鏂囦欢澶у皬涓嶈兘瓒呰繃${maxSizeMb}MB`);
ElMessage.warning(`专家简介/串场文件大小不能超过${maxSizeMb}MB`);
return false;
}
return true;
};
const beforeAgendaUpload = (rawFile: any, maxSizeMb?: number) => {
if (!validateMaterialFile((rawFile as File) || null, "浼氳鏃ョ▼", maxSizeMb)) {
if (!validateMaterialFile((rawFile as File) || null, "会议日程", maxSizeMb)) {
return false;
}
if (agendaDocRows.value.length >= 5) {
@ -3455,23 +3455,23 @@ const beforeAgendaUpload = (rawFile: any, maxSizeMb?: number) => {
const beforeSignInUpload = (rawFile: any, maxSizeMb?: number) =>
validateMaterialFile((rawFile as File) || null, "签到表", maxSizeMb);
const beforeThemePhotoUpload = (rawFile: any, maxSizeMb?: number) =>
validateImageFile((rawFile as File) || null, "涓婚鐓х墖", maxSizeMb);
validateImageFile((rawFile as File) || null, "主题照片", maxSizeMb);
const beforeInvitationUpload = (rawFile: any, maxSizeMb?: number) =>
validateMaterialFile((rawFile as File) || null, "閭€璇峰嚱", maxSizeMb);
validateMaterialFile((rawFile as File) || null, "邀请函", maxSizeMb);
const beforePhotoUpload: UploadProps["beforeUpload"] = (rawFile) =>
validateMaterialFile((rawFile as File) || null, "鐜板満鐓х墖");
validateMaterialFile((rawFile as File) || null, "现场照片");
const beforeLaborProtocolUpload: UploadProps["beforeUpload"] = (rawFile) =>
validateMaterialFile((rawFile as File) || null, "鍗忚鏂囦欢");
validateMaterialFile((rawFile as File) || null, "协议文件");
const beforeLaborInvoiceUpload: UploadProps["beforeUpload"] = (rawFile) =>
validateMaterialFile((rawFile as File) || null, "鍔冲姟鍙戠エ");
validateMaterialFile((rawFile as File) || null, "劳务发票");
const beforeMeetingInvoiceUpload: UploadProps["beforeUpload"] = (rawFile) =>
validateMaterialFile((rawFile as File) || null, "浼氳鍙戠エ闄勪欢");
validateMaterialFile((rawFile as File) || null, "会议发票附件");
const beforeExpertProfileUpload = (rawFile: any, maxSizeMb?: number) =>
validateExpertProfileFile((rawFile as File) || null, maxSizeMb);
const onAgendaFileChange = (uploadFile: any) => {
const file = (uploadFile?.raw as File) || null;
agendaFile.value = validateMaterialFile(file, "浼氳鏃ョ▼") ? file : null;
agendaFile.value = validateMaterialFile(file, "会议日程") ? file : null;
};
const onSignInFileChange = (uploadFile: any) => {
@ -3481,7 +3481,7 @@ const onSignInFileChange = (uploadFile: any) => {
const onInvitationFileChange = (uploadFile: any) => {
const file = (uploadFile?.raw as File) || null;
invitationFile.value = validateMaterialFile(file, "閭€璇峰嚱") ? file : null;
invitationFile.value = validateMaterialFile(file, "邀请函") ? file : null;
};
const handleAgendaPictureChange = async (uploadFile: any, uploadFiles: any, maxSizeMb?: number) => {
@ -3490,7 +3490,7 @@ const handleAgendaPictureChange = async (uploadFile: any, uploadFiles: any, maxS
return;
}
const file = (uploadFile?.raw as File) || null;
agendaFile.value = validateMaterialFile(file, "浼氳鏃ョ▼", maxSizeMb) ? file : null;
agendaFile.value = validateMaterialFile(file, "会议日程", maxSizeMb) ? file : null;
if (!agendaFile.value) {
return;
}
@ -3516,7 +3516,7 @@ const handleThemePhotoPictureChange = async (uploadFile: any, uploadFiles: any,
return;
}
const file = (uploadFile?.raw as File) || null;
themePhotoFile.value = validateImageFile(file, "涓婚鐓х墖", maxSizeMb) ? file : null;
themePhotoFile.value = validateImageFile(file, "主题照片", maxSizeMb) ? file : null;
if (!themePhotoFile.value) {
return;
}
@ -3525,7 +3525,7 @@ const handleThemePhotoPictureChange = async (uploadFile: any, uploadFiles: any,
const handleInvitationPictureChange = async (uploadFile: any, uploadFiles: any, maxSizeMb?: number) => {
const file = (uploadFile?.raw as File) || null;
invitationFile.value = validateMaterialFile(file, "閭€璇峰嚱", maxSizeMb) ? file : null;
invitationFile.value = validateMaterialFile(file, "邀请函", maxSizeMb) ? file : null;
if (!invitationFile.value) {
return;
}
@ -3553,7 +3553,7 @@ const handleExpertProfileFileChange = async (uploadFile: any, uploadFiles: any,
await putToSignedUrl(uploadUrl, file, signResp?.data?.contentType || file.type);
expertProfileForm.value.fileName = file.name;
expertProfileForm.value.ossKey = objectKey;
ElMessage.success("涓撳绠€浠?涓插満鏂囦欢涓婁紶鎴愬姛");
ElMessage.success("专家简介/串场文件上传成功");
};
const handleExpertProfileFilePreview: UploadProps["onPreview"] = async (uploadFile) => {
const ossKey = String(expertProfileForm.value.ossKey || "").trim();
@ -3673,7 +3673,7 @@ const uploadDocFile = async (target: "agenda" | "signIn" | "themePhoto" | "invit
? themePhotoFile.value
: invitationFile.value;
if (!file) {
ElMessage.warning("璇峰厛閫夋嫨鏂囦欢");
ElMessage.warning("请先选择文件");
return;
}
const signResp = await fetchMeetingMaterialUploadSign(selectedMeetingId.value, "WRITE_OFF_DOCS", {
@ -3711,7 +3711,7 @@ const uploadDocFile = async (target: "agenda" | "signIn" | "themePhoto" | "invit
const onPhotoFileChange = async (uploadFile: any) => {
const file = (uploadFile?.raw as File) || null;
if (!validateMaterialFile(file, "鐜板満鐓х墖") || !file) {
if (!validateMaterialFile(file, "现场照片") || !file) {
return;
}
const exists = photoUploadFiles.value.some((x) => x.name === file.name && x.size === file.size && x.lastModified === file.lastModified);
@ -3737,7 +3737,7 @@ const onLaborProtocolFileChange = async (uploadFile: any) => {
return;
}
const file = (uploadFile?.raw as File) || null;
laborProtocolUploadFile.value = validateMaterialFile(file, "鍗忚鏂囦欢") ? file : null;
laborProtocolUploadFile.value = validateMaterialFile(file, "协议文件") ? file : null;
if (!laborProtocolUploadFile.value || laborProtocolAutoUploading.value) {
return;
}
@ -3755,7 +3755,7 @@ const onLaborInvoiceFileChange = async (uploadFile: any) => {
return;
}
const file = (uploadFile?.raw as File) || null;
if (!validateMaterialFile(file, "鍔冲姟鍙戠エ") || !file) {
if (!validateMaterialFile(file, "劳务发票") || !file) {
return;
}
const exists = laborInvoiceUploadFiles.value.some((x) => x.name === file.name && x.size === file.size && x.lastModified === file.lastModified);
@ -3777,7 +3777,7 @@ const onLaborInvoiceFileChange = async (uploadFile: any) => {
const onInvoiceFileChange = async (uploadFile: any) => {
const file = (uploadFile?.raw as File) || null;
if (!validateMaterialFile(file, "鍙戠エ闄勪欢") || !file) {
if (!validateMaterialFile(file, "发票附件") || !file) {
return;
}
const exists = invoiceUploadFiles.value.some((x) => x.name === file.name && x.size === file.size && x.lastModified === file.lastModified);
@ -3811,7 +3811,7 @@ const onMeetingInvoiceSectionFileChange = async (
return;
}
const file = (uploadFile?.raw as File) || null;
if (!validateMaterialFile(file, "浼氳鍙戠エ闄勪欢") || !file || !selectedMeetingId.value) {
if (!validateMaterialFile(file, "会议发票附件") || !file || !selectedMeetingId.value) {
return;
}
const uploadTaskKey = `${sectionCode}:${fieldKey}`;
@ -3841,7 +3841,7 @@ const onMeetingInvoiceSectionFileChange = async (
ocr: fieldKey === "invoiceFile" ? { status: "idle" } : undefined,
});
await cacheDocPreviewUrl(objectKey);
ElMessage.success("浼氳鍙戠エ闄勪欢涓婁紶鎴愬姛");
ElMessage.success("会议发票附件上传成功");
if (fieldKey === "invoiceFile") {
await runMeetingInvoiceOcr(sectionCode, fieldKey);
}
@ -3971,7 +3971,7 @@ const uploadPhotoFile = async () => {
return;
}
if (photoUploadFiles.value.length === 0) {
ElMessage.warning("璇峰厛閫夋嫨鐓х墖");
ElMessage.warning("请先选择照片");
return;
}
const expertId = Number(selectedExpertMaterialId.value);
@ -3998,7 +3998,7 @@ const uploadPhotoFile = async () => {
successCount++;
}
photoUploadFiles.value = [];
ElMessage.success(`鐜板満鐓х墖涓婁紶鎴愬姛锛${successCount}寮狅級`);
ElMessage.success(`现场照片上传成功(${successCount}张)`);
};
const uploadLaborProtocolFile = async () => {
@ -4006,7 +4006,7 @@ const uploadLaborProtocolFile = async () => {
return;
}
if (!laborProtocolUploadFile.value) {
ElMessage.warning("璇峰厛閫夋嫨鍗忚鏂囦欢");
ElMessage.warning("请先选择协议文件");
return;
}
const signResp = await fetchMeetingMaterialUploadSign(selectedMeetingId.value, "EXPERT_LIST", {
@ -4035,7 +4035,7 @@ const uploadLaborProtocolFile = async () => {
}
}
laborProtocolUploadFile.value = null;
ElMessage.success("鍔冲姟鍗忚涓婁紶鎴愬姛");
ElMessage.success("劳务协议上传成功");
};
const uploadLaborInvoiceFile = async () => {
@ -4043,7 +4043,7 @@ const uploadLaborInvoiceFile = async () => {
return;
}
if (laborInvoiceUploadFiles.value.length === 0) {
ElMessage.warning("璇峰厛閫夋嫨鍔冲姟鍙戠エ");
ElMessage.warning("请先选择劳务发票");
return;
}
const localFile = laborInvoiceUploadFiles.value[0];
@ -4076,7 +4076,7 @@ const uploadLaborInvoiceFile = async () => {
row.invoicePreviewUrl = previewUrl;
}
laborInvoiceUploadFiles.value.shift();
ElMessage.success("鍔冲姟鍙戠エ涓婁紶鎴愬姛");
ElMessage.success("劳务发票上传成功");
if (objectKey) {
await runLaborInvoiceOcr(objectKey);
}
@ -4087,11 +4087,11 @@ const uploadInvoiceFile = async () => {
return;
}
if (invoiceUploadFiles.value.length === 0) {
ElMessage.warning("璇峰厛閫夋嫨鍙戠エ闄勪欢");
ElMessage.warning("请先选择发票附件");
return;
}
if (!invoiceDraft.value.invoiceNo.trim()) {
ElMessage.warning("璇峰~鍐欏彂绁ㄥ彿");
ElMessage.warning("请填写发票号");
return;
}
const expertId = Number(selectedExpertMaterialId.value);
@ -4124,8 +4124,8 @@ const uploadInvoiceFile = async () => {
invoiceDraft.value.invoiceNo = "";
invoiceDraft.value.amountCent = 0;
invoiceDraft.value.taxCent = 0;
invoiceDraft.value.expenseType = "鍏朵粬";
ElMessage.success(`鍙戠エ闄勪欢涓婁紶鎴愬姛锛${successCount}寮狅級`);
invoiceDraft.value.expenseType = "其他";
ElMessage.success(`发票附件上传成功(${successCount}张)`);
};
const removeSelectedExpertPhoto = (index: number) => {
@ -4196,7 +4196,7 @@ const removeMeetingInvoiceSectionFile = (
const handleViewOss = async (ossKey: string) => {
if (!ossKey) {
ElMessage.warning("OSSKey 涓虹┖");
ElMessage.warning("OSSKey 为空");
return;
}
try {
@ -4205,10 +4205,10 @@ const handleViewOss = async (ossKey: string) => {
if (signedUrl) {
window.open(signedUrl, "_blank");
} else {
ElMessage.warning("鏈幏鍙栧埌鏌ョ湅閾炬帴");
ElMessage.warning("未获取到查看链接");
}
} catch (_e) {
ElMessage.error("鏌ョ湅澶辫触锛岃妫€鏌?file.download 鏉冮檺");
ElMessage.error("查看失败,请检查 file.download 权限");
}
};
@ -4325,7 +4325,7 @@ const loadMaterialReviews = async (
return;
}
}
materialReviewNotice.value = "褰撳墠妯″潡鏆傛棤鏉$洰瀹℃牳鏄庣粏";
materialReviewNotice.value = "当前模块暂无条目审核明细";
} catch (_e) {
if (typeof requestId === "number" && !isMaterialLoadActive(requestId, meetingId, moduleCode)) {
return;
@ -4364,7 +4364,7 @@ const loadMaterialModule = async (
) => {
const backendModuleCode = resolveBackendMaterialModuleCode(moduleCode);
selectedModuleCode.value = moduleCode;
materialDialogTitle.value = moduleTitleMap[moduleCode] || "浼氳璧勬枡妯″潡";
materialDialogTitle.value = moduleTitleMap[moduleCode] || "会议资料模块";
resetMaterialForms();
if (moduleCode === "EXPERT_LIST") {
await loadBasicInfoForMaterial(meetingId, requestId);
@ -4809,7 +4809,7 @@ const validateMeetingInvoiceRequired = () => {
const amountCent = toCent(amount);
if (amountCent > maxCateringCent) {
ElMessage.warning(
`鈥滈楗槑缁嗏€濋噾棰濅笉鑳借秴杩囦細璁绠?椁愯垂鍗犳瘮锛堜笂闄${toYuan(maxCateringCent)}鍏冿級`,
`“餐饮明细”金额不能超过会议预算 × 餐费占比(上限${toYuan(maxCateringCent)}元)`,
);
return false;
}
@ -4823,7 +4823,7 @@ const validateExpertProfileRequired = () => {
return true;
}
if (!String(expertProfileForm.value.ossKey || "").trim()) {
ElMessage.warning("璇蜂笂浼犱笓瀹剁畝浠?涓插満鏂囦欢");
ElMessage.warning("请上传专家简介/串场文件");
return false;
}
return true;
@ -5019,7 +5019,7 @@ const handleSaveMaterial = async () => {
remark: materialForm.value.remark,
},
);
ElMessage.success("妯″潡淇濆瓨鎴愬姛");
ElMessage.success("模块保存成功");
initialMaterialContentJson.value = buildContentJson();
return true;
} catch (e) {
@ -5055,7 +5055,7 @@ const handleSubmitMaterial = async () => {
remark: materialForm.value.remark,
},
);
ElMessage.success("妯″潡鎻愪氦鎴愬姛");
ElMessage.success("模块提交成功");
materialDialogVisible.value = false;
};