diff --git a/backend/src/main/java/com/imeeting/dto/biz/MeetingVO.java b/backend/src/main/java/com/imeeting/dto/biz/MeetingVO.java index b851359..6603988 100644 --- a/backend/src/main/java/com/imeeting/dto/biz/MeetingVO.java +++ b/backend/src/main/java/com/imeeting/dto/biz/MeetingVO.java @@ -57,6 +57,18 @@ public class MeetingVO { private String lastUserPrompt; @Schema(description = "分析结果") private Map analysis; + @Schema(description = "最近一次总结尝试任务 ID") + private Long latestSummaryAttemptTaskId; + @Schema(description = "最近一次总结尝试任务状态") + private Integer latestSummaryAttemptStatus; + @Schema(description = "最近一次总结尝试错误信息") + private String latestSummaryAttemptErrorMsg; + @Schema(description = "最近一次章节尝试任务 ID") + private Long latestChapterAttemptTaskId; + @Schema(description = "最近一次章节尝试任务状态") + private Integer latestChapterAttemptStatus; + @Schema(description = "最近一次章节尝试错误信息") + private String latestChapterAttemptErrorMsg; @Schema(description = "会议状态") private Integer status; diff --git a/backend/src/main/java/com/imeeting/service/biz/MeetingTranscriptChapterService.java b/backend/src/main/java/com/imeeting/service/biz/MeetingTranscriptChapterService.java index 896bf45..5d10aa6 100644 --- a/backend/src/main/java/com/imeeting/service/biz/MeetingTranscriptChapterService.java +++ b/backend/src/main/java/com/imeeting/service/biz/MeetingTranscriptChapterService.java @@ -15,6 +15,8 @@ public interface MeetingTranscriptChapterService { List> listCurrentChapterAnalysis(Long meetingId); + List> listDisplayChapterAnalysis(Meeting meeting); + void invalidateCurrentVersion(Long meetingId); MeetingTranscriptChapterVersion importExternalChapters(Meeting meeting, AiTask sourceTask, MeetingTranscriptChapterImportDTO command); @@ -22,4 +24,6 @@ public interface MeetingTranscriptChapterService { MeetingTranscriptSourceVO buildTranscriptSource(Long meetingId); MeetingTranscriptChapterVersion getCurrentVersion(Long meetingId); + + String loadCurrentChapterMarkdown(Meeting meeting); } diff --git a/backend/src/main/java/com/imeeting/service/biz/MeetingTranscriptFileService.java b/backend/src/main/java/com/imeeting/service/biz/MeetingTranscriptFileService.java index 5915ccb..ef06872 100644 --- a/backend/src/main/java/com/imeeting/service/biz/MeetingTranscriptFileService.java +++ b/backend/src/main/java/com/imeeting/service/biz/MeetingTranscriptFileService.java @@ -8,4 +8,6 @@ public interface MeetingTranscriptFileService { void initializeTranscriptFileIfAbsent(Long meetingId); MeetingTranscriptExportResult exportTranscript(Meeting meeting, MeetingVO meetingDetail); + + String loadTranscriptMarkdown(Meeting meeting, MeetingVO meetingDetail); } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingCommandServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingCommandServiceImpl.java index de79b71..31251cf 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingCommandServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingCommandServiceImpl.java @@ -735,6 +735,11 @@ public class MeetingCommandServiceImpl implements MeetingCommandService { } meeting.setStatus(2); meetingService.updateById(meeting); + if ("EXTERNAL_N8N".equalsIgnoreCase(summaryOrchestrationMode)) { + updateMeetingProgress(meetingId, 95, "等待外部章节与总结编排...", 0); + } else { + updateMeetingProgress(meetingId, 85, "重新总结已提交,正在生成章节...", 0); + } dispatchSummaryTaskAfterCommit(meetingId, meeting.getTenantId(), meeting.getCreatorId()); } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingDomainSupport.java b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingDomainSupport.java index c4ec8a6..21c5a78 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingDomainSupport.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingDomainSupport.java @@ -339,6 +339,7 @@ public class MeetingDomainSupport { vo.setSummaryContent(meetingSummaryFileService.loadSummaryContent(meeting)); vo.setAnalysis(meetingSummaryFileService.loadSummaryAnalysis(meeting)); vo.setLastUserPrompt(resolveLastSummaryUserPrompt(meeting)); + fillLatestTaskAttemptInfo(meeting, vo); } } @@ -379,6 +380,41 @@ public class MeetingDomainSupport { .last("LIMIT 1")); } + private void fillLatestTaskAttemptInfo(Meeting meeting, com.imeeting.dto.biz.MeetingVO vo) { + AiTask latestSummaryAttempt = resolveLatestTaskAttempt(meeting, "SUMMARY"); + if (latestSummaryAttempt != null) { + vo.setLatestSummaryAttemptTaskId(latestSummaryAttempt.getId()); + vo.setLatestSummaryAttemptStatus(latestSummaryAttempt.getStatus()); + vo.setLatestSummaryAttemptErrorMsg(normalizeTaskError(latestSummaryAttempt.getErrorMsg())); + } + + AiTask latestChapterAttempt = resolveLatestTaskAttempt(meeting, "CHAPTER"); + if (latestChapterAttempt != null) { + vo.setLatestChapterAttemptTaskId(latestChapterAttempt.getId()); + vo.setLatestChapterAttemptStatus(latestChapterAttempt.getStatus()); + vo.setLatestChapterAttemptErrorMsg(normalizeTaskError(latestChapterAttempt.getErrorMsg())); + } + } + + private AiTask resolveLatestTaskAttempt(Meeting meeting, String taskType) { + if (meeting == null || meeting.getId() == null) { + return null; + } + return aiTaskService.getOne(new LambdaQueryWrapper() + .eq(AiTask::getMeetingId, meeting.getId()) + .eq(AiTask::getTaskType, taskType) + .orderByDesc(AiTask::getId) + .last("LIMIT 1")); + } + + private String normalizeTaskError(String errorMsg) { + if (errorMsg == null) { + return null; + } + String normalized = errorMsg.trim(); + return normalized.isEmpty() ? null : normalized; + } + private record AudioRelocationPlan(Path sourcePath, Path targetPath, Path backupPath, String relocatedUrl) { } } diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingQueryServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingQueryServiceImpl.java index 6c7c9e1..3187e65 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingQueryServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingQueryServiceImpl.java @@ -105,7 +105,11 @@ public class MeetingQueryServiceImpl implements MeetingQueryService { @Override public List> getChapters(Long meetingId) { - return meetingTranscriptChapterService.listCurrentChapterAnalysis(meetingId); + Meeting meeting = meetingService.getById(meetingId); + if (meeting == null) { + return List.of(); + } + return meetingTranscriptChapterService.listDisplayChapterAnalysis(meeting); } @Override diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingTranscriptChapterServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingTranscriptChapterServiceImpl.java index 9810f11..ed80a5c 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingTranscriptChapterServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingTranscriptChapterServiceImpl.java @@ -15,6 +15,7 @@ import com.imeeting.entity.biz.Meeting; import com.imeeting.entity.biz.MeetingTranscript; import com.imeeting.entity.biz.MeetingTranscriptChapter; import com.imeeting.entity.biz.MeetingTranscriptChapterVersion; +import com.imeeting.mapper.biz.AiTaskMapper; import com.imeeting.mapper.biz.MeetingTranscriptChapterMapper; import com.imeeting.mapper.biz.MeetingTranscriptChapterVersionMapper; import com.imeeting.mapper.biz.MeetingTranscriptMapper; @@ -71,6 +72,7 @@ public class MeetingTranscriptChapterServiceImpl implements MeetingTranscriptCha private final MeetingTranscriptMapper transcriptMapper; private final MeetingTranscriptChapterVersionMapper versionMapper; private final MeetingTranscriptChapterMapper chapterMapper; + private final AiTaskMapper aiTaskMapper; private final ObjectMapper objectMapper; private AiModelService aiModelService; @@ -129,10 +131,26 @@ public class MeetingTranscriptChapterServiceImpl implements MeetingTranscriptCha if (current == null) { return List.of(); } + return listVersionChapterAnalysis(meetingId, current.getId()); + } + + @Override + public List> listDisplayChapterAnalysis(Meeting meeting) { + if (meeting == null || meeting.getId() == null) { + return List.of(); + } + MeetingTranscriptChapterVersion displayVersion = resolveDisplayVersion(meeting); + if (displayVersion == null) { + return List.of(); + } + return listVersionChapterAnalysis(meeting.getId(), displayVersion.getId()); + } + + private List> listVersionChapterAnalysis(Long meetingId, Long versionId) { List transcripts = loadRawTranscripts(meetingId); Map transcriptById = transcripts.stream() .collect(Collectors.toMap(MeetingTranscript::getId, item -> item, (left, right) -> left, LinkedHashMap::new)); - return loadVersionChapters(current.getId()).stream() + return loadVersionChapters(versionId).stream() .map(chapter -> toChapterAnalysis(chapter, transcriptById)) .toList(); } @@ -200,6 +218,29 @@ public class MeetingTranscriptChapterServiceImpl implements MeetingTranscriptCha return findCurrentVersion(meetingId); } + @Override + public String loadCurrentChapterMarkdown(Meeting meeting) { + if (meeting == null || meeting.getId() == null) { + throw new RuntimeException("Meeting not found"); + } + MeetingTranscriptChapterVersion current = findCurrentVersion(meeting.getId()); + if (current == null) { + return ""; + } + List transcripts = loadRawTranscripts(meeting.getId()); + Map transcriptById = transcripts.stream() + .collect(Collectors.toMap(MeetingTranscript::getId, item -> item, (left, right) -> left, LinkedHashMap::new)); + List chapters = loadVersionChapters(current.getId()); + String relativePath = writeCurrentChapterMarkdown(meeting, current, chapters, transcriptById); + try { + String basePath = uploadPath.endsWith("/") || uploadPath.endsWith("\\") ? uploadPath : uploadPath + "/"; + Path targetPath = Paths.get(basePath, relativePath.replace("\\", "/")); + return Files.exists(targetPath) ? Files.readString(targetPath, StandardCharsets.UTF_8) : ""; + } catch (Exception ex) { + throw new RuntimeException("Failed to load meeting chapter markdown", ex); + } + } + private MeetingTranscriptChapterVersion generateInternalVersion(Meeting meeting, AiTask summaryTask, List transcripts, @@ -397,6 +438,49 @@ public class MeetingTranscriptChapterServiceImpl implements MeetingTranscriptCha return version; } + private MeetingTranscriptChapterVersion resolveDisplayVersion(Meeting meeting) { + Long chapterVersionId = extractChapterVersionId(resolveDisplaySummaryTask(meeting)); + if (chapterVersionId != null) { + MeetingTranscriptChapterVersion version = versionMapper.selectById(chapterVersionId); + if (version != null && Objects.equals(version.getMeetingId(), meeting.getId())) { + return version; + } + } + return findCurrentVersion(meeting.getId()); + } + + private AiTask resolveDisplaySummaryTask(Meeting meeting) { + if (meeting.getLatestSummaryTaskId() != null) { + AiTask task = aiTaskMapper.selectById(meeting.getLatestSummaryTaskId()); + if (task != null + && Objects.equals(task.getMeetingId(), meeting.getId()) + && "SUMMARY".equals(task.getTaskType()) + && Integer.valueOf(2).equals(task.getStatus())) { + return task; + } + } + return aiTaskMapper.selectOne(new LambdaQueryWrapper() + .eq(AiTask::getMeetingId, meeting.getId()) + .eq(AiTask::getTaskType, "SUMMARY") + .eq(AiTask::getStatus, 2) + .orderByDesc(AiTask::getId) + .last("LIMIT 1")); + } + + private Long extractChapterVersionId(AiTask summaryTask) { + if (summaryTask == null || summaryTask.getResponseData() == null) { + return null; + } + Object summarySource = summaryTask.getResponseData().get("summarySource"); + if (summarySource instanceof Map sourceMap) { + Long versionId = longValue(sourceMap.get("chapterVersionId")); + if (versionId != null) { + return versionId; + } + } + return longValue(summaryTask.getResponseData().get("chapterVersionId")); + } + private MeetingSummarySource buildSummarySource(Meeting meeting, MeetingTranscriptChapterVersion version, List transcripts, diff --git a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingTranscriptFileServiceImpl.java b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingTranscriptFileServiceImpl.java index e476b66..2d4aab2 100644 --- a/backend/src/main/java/com/imeeting/service/biz/impl/MeetingTranscriptFileServiceImpl.java +++ b/backend/src/main/java/com/imeeting/service/biz/impl/MeetingTranscriptFileServiceImpl.java @@ -49,15 +49,16 @@ public class MeetingTranscriptFileServiceImpl implements MeetingTranscriptFileSe if (meeting == null) { return; } - writeTranscriptFile(meeting, null); + loadTranscriptMarkdown(meeting, null); } @Override public MeetingTranscriptExportResult exportTranscript(Meeting meeting, MeetingVO meetingDetail) { if (meeting == null || meeting.getId() == null) { - throw new RuntimeException("会议不存在"); + throw new RuntimeException("Meeting not found"); } - byte[] content = writeTranscriptFile(meeting, meetingDetail); + String markdown = loadTranscriptMarkdown(meeting, meetingDetail); + byte[] content = markdown.getBytes(StandardCharsets.UTF_8); String safeTitle = sanitizeFileName( meetingDetail != null ? meetingDetail.getTitle() : meeting.getTitle(), "meeting-transcript-" + meeting.getId() @@ -65,7 +66,8 @@ public class MeetingTranscriptFileServiceImpl implements MeetingTranscriptFileSe return new MeetingTranscriptExportResult(content, CONTENT_TYPE, safeTitle + "-Transcript.md"); } - private byte[] writeTranscriptFile(Meeting meeting, MeetingVO meetingDetail) { + @Override + public String loadTranscriptMarkdown(Meeting meeting, MeetingVO meetingDetail) { try { Path transcriptPath = buildTranscriptPath(meeting.getId()); Path parent = transcriptPath.getParent(); @@ -74,9 +76,9 @@ public class MeetingTranscriptFileServiceImpl implements MeetingTranscriptFileSe } String markdown = buildTranscriptMarkdown(meeting, meetingDetail); Files.writeString(transcriptPath, markdown, StandardCharsets.UTF_8); - return markdown.getBytes(StandardCharsets.UTF_8); + return markdown; } catch (IOException ex) { - throw new RuntimeException("写入会议转录文件失败", ex); + throw new RuntimeException("Failed to write meeting transcript markdown", ex); } } diff --git a/frontend/src/api/business/meeting.ts b/frontend/src/api/business/meeting.ts index 684dfdf..9631db4 100644 --- a/frontend/src/api/business/meeting.ts +++ b/frontend/src/api/business/meeting.ts @@ -33,6 +33,12 @@ export interface MeetingVO { keyPoints?: Array<{ title?: string; summary?: string; speaker?: string; time?: string }>; todos?: string[]; }; + latestSummaryAttemptTaskId?: number; + latestSummaryAttemptStatus?: number; + latestSummaryAttemptErrorMsg?: string; + latestChapterAttemptTaskId?: number; + latestChapterAttemptStatus?: number; + latestChapterAttemptErrorMsg?: string; status: number; displayStatus?: number; realtimeSessionStatus?: RealtimeMeetingSessionStatus["status"]; @@ -269,11 +275,12 @@ export interface PublicMeetingPreviewVO { chapters?: MeetingChapterVO[]; } -export const getMeetingDetail = (id: number) => { +export const getMeetingDetail = (id: number, options?: { suppressErrorToast?: boolean }) => { return http.get<{ code: string; data: MeetingVO; msg: string }>( `/api/biz/meeting/${id}`, { timeout: MEETING_DETAIL_TIMEOUT, + suppressErrorToast: options?.suppressErrorToast, } ); }; @@ -403,9 +410,12 @@ export interface MeetingProgress { eta?: number; } -export const getMeetingProgress = (id: number) => { +export const getMeetingProgress = (id: number, options?: { suppressErrorToast?: boolean }) => { return http.get<{ code: string; data: MeetingProgress; msg: string }>( - `/api/biz/meeting/${id}/progress` + `/api/biz/meeting/${id}/progress`, + { + suppressErrorToast: options?.suppressErrorToast, + } ); }; diff --git a/frontend/src/api/http.ts b/frontend/src/api/http.ts index 3877f2f..f58ad78 100644 --- a/frontend/src/api/http.ts +++ b/frontend/src/api/http.ts @@ -1,6 +1,16 @@ import axios from "axios"; import { message } from "antd"; +declare module "axios" { + interface AxiosRequestConfig { + suppressErrorToast?: boolean; + } + + interface InternalAxiosRequestConfig { + suppressErrorToast?: boolean; + } +} + const http = axios.create({ baseURL: "/", timeout: 15000 @@ -122,7 +132,9 @@ http.interceptors.response.use( const body = resp.data; if (body && !isApiSuccessCode(body.code)) { const errorMsg = body.msg || "请求失败"; - message.error(errorMsg); + if (!resp.config?.suppressErrorToast) { + message.error(errorMsg); + } const err = new Error(errorMsg); (err as any).code = body.code; (err as any).msg = body.msg; @@ -154,7 +166,9 @@ http.interceptors.response.use( const body = error.response?.data; const errorMsg = body?.msg || error.message || "网络异常"; - message.error(errorMsg); + if (!originalRequest.suppressErrorToast) { + message.error(errorMsg); + } if (body && body.msg) { const err = new Error(body.msg); diff --git a/frontend/src/components/shared/PageContainer/index.tsx b/frontend/src/components/shared/PageContainer/index.tsx index d0584d7..f8fd532 100644 --- a/frontend/src/components/shared/PageContainer/index.tsx +++ b/frontend/src/components/shared/PageContainer/index.tsx @@ -85,7 +85,6 @@ const PageContainer: React.FC = ({ style={{ flex: 1, minHeight: 0, - overflow: 'auto', display: 'flex', flexDirection: 'column' }} diff --git a/frontend/src/pages/business/MeetingDetail.tsx b/frontend/src/pages/business/MeetingDetail.tsx index 45296e9..26ca2fc 100644 --- a/frontend/src/pages/business/MeetingDetail.tsx +++ b/frontend/src/pages/business/MeetingDetail.tsx @@ -359,20 +359,31 @@ const MeetingProgressDisplay: React.FC<{ const [progress, setProgress] = useState(null); useEffect(() => { + let completed = false; + const fetchProgress = async () => { + if (completed) { + return; + } try { const [progressRes, detailRes] = await Promise.all([ - getMeetingProgress(meetingId), - getMeetingDetail(meetingId), + getMeetingProgress(meetingId, { suppressErrorToast: true }), + getMeetingDetail(meetingId, { suppressErrorToast: true }), ]); if (detailRes.data?.data) { onProgressUpdate?.(detailRes.data.data); + if (detailRes.data.data.status !== 1 && detailRes.data.data.status !== 2) { + completed = true; + onComplete(); + return; + } } if (progressRes.data?.data) { setProgress(progressRes.data.data); - if (progressRes.data.data.percent === 100) { + if (progressRes.data.data.percent === 100 || progressRes.data.data.percent < 0) { + completed = true; onComplete(); } } @@ -383,7 +394,10 @@ const MeetingProgressDisplay: React.FC<{ fetchProgress(); const timer = setInterval(fetchProgress, 3000); - return () => clearInterval(timer); + return () => { + completed = true; + clearInterval(timer); + }; }, [meetingId, onComplete, onProgressUpdate]); const percent = progress?.percent || 0; @@ -902,6 +916,38 @@ const MeetingDetail: React.FC = () => { const canRetrySummary = isOwner && transcripts.length > 0 && meeting?.status !== 1 && meeting?.status !== 2; const canRetryTranscription = isOwner && meeting?.status === 4 && transcripts.length === 0 && !!meeting?.audioUrl; + const generationFailureNotice = useMemo(() => { + if (!meeting || meeting.status !== 4) { + return null; + } + + const hasFallbackContent = Boolean(meeting.summaryContent) || meetingChapters.length > 0; + if (meeting.latestChapterAttemptStatus === 3) { + const detail = meeting.latestChapterAttemptErrorMsg || '章节生成失败'; + return { + key: `chapter-${meeting.latestChapterAttemptTaskId ?? 'latest'}`, + title: '本次重新总结失败', + description: hasFallbackContent + ? `章节生成失败,当前展示的是上一次成功的摘要和 AI 目录。失败原因:${detail}` + : `章节生成失败,且当前没有可展示的历史摘要或 AI 目录。失败原因:${detail}`, + hasFallbackContent, + }; + } + + if (meeting.latestSummaryAttemptStatus === 3) { + const detail = meeting.latestSummaryAttemptErrorMsg || '总结生成失败'; + return { + key: `summary-${meeting.latestSummaryAttemptTaskId ?? 'latest'}`, + title: '本次重新总结失败', + description: hasFallbackContent + ? `总结生成失败,当前展示的是上一次成功的摘要和 AI 目录。失败原因:${detail}` + : `总结生成失败,且当前没有可展示的历史摘要或 AI 目录。失败原因:${detail}`, + hasFallbackContent, + }; + } + + return null; + }, [meeting, meetingChapters.length]); const emptyTranscriptFailureNotice = useMemo(() => { if (!meeting || meeting.status !== 4 || transcripts.length > 0) { return null; @@ -916,6 +962,24 @@ const MeetingDetail: React.FC = () => { }; }, [canRetryTranscription, meeting, transcripts.length]); + useEffect(() => { + if (!generationFailureNotice) { + return; + } + const acknowledgedKey = `meeting-failure-ack:${generationFailureNotice.key}`; + if (sessionStorage.getItem(acknowledgedKey) === '1') { + return; + } + Modal.warning({ + title: generationFailureNotice.title, + content: generationFailureNotice.description, + okText: '我知道了', + onOk: () => { + sessionStorage.setItem(acknowledgedKey, '1'); + }, + }); + }, [generationFailureNotice]); + useEffect(() => { if (!playbackAudioUrl) { setShowFloatingTranscriptPlayer(false); @@ -1831,11 +1895,26 @@ const MeetingDetail: React.FC = () => { + {generationFailureNotice && ( + + )} + {meeting.status === 2 ? (
fetchData(meeting.id)} + onProgressUpdate={(updated) => { + if (updated.status !== meeting.status) { + void fetchData(updated.id); + } + }} compact />
@@ -2029,6 +2108,15 @@ const MeetingDetail: React.FC = () => {
{workspaceTab === 'catalog' ? (
+ {generationFailureNotice && !generationFailureNotice.hasFallbackContent && ( + + )} {catalogChapterLinks.length ? ( catalogChapterLinks.map((chapter, index) => (
void) => { if (meeting.status !== 1 && meeting.status !== 2) return; const fetchProgress = async () => { try { - const res = await getMeetingProgress(meeting.id); + const res = await getMeetingProgress(meeting.id, { suppressErrorToast: true }); if (res.data && res.data.data) { setProgress(res.data.data); - if (res.data.data.percent === 100 && onComplete) { + if ((res.data.data.percent === 100 || res.data.data.percent < 0) && onComplete) { onComplete(); } } @@ -604,35 +604,44 @@ const Meetings: React.FC = () => { } > - - {displayMode === 'card' ? ( - - { - const config = statusConfig[item.displayStatus ?? item.status] || statusConfig[0]; - return ; - }} - locale={{ emptyText: }} - /> - - ) : ( - ({ - onClick: () => handleOpenMeeting(record), - style: { cursor: 'pointer' } - })} - locale={{ emptyText: }} - /> - )} + +
+ {displayMode === 'card' ? ( + + { + const config = statusConfig[item.displayStatus ?? item.status] || statusConfig[0]; + return ; + }} + locale={{ emptyText: }} + /> + + ) : ( +
({ + onClick: () => handleOpenMeeting(record), + style: { cursor: 'pointer' } + })} + locale={{ emptyText: }} + /> + )} + - { setCurrent(p); setSize(s); }} /> +
+ { setCurrent(p); setSize(s); }} /> +